blob: ffe6137f69e6eaf049d4eeb24a5db371d156a79b [file] [log] [blame]
Giulio Grassi624f6c62014-02-18 19:42:14 +01001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyev319f2c82015-01-07 14:56:53 -08003 * Copyright (c) 2014-2015, 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"
28
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
Junxiao Shi39cd6332014-11-06 21:53:18 -070078 m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE),
Giulio Grassi624f6c62014-02-18 19:42:14 +010079 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
Giulio Grassi624f6c62014-02-18 19:42:14 +0100120size_t
121UdpChannel::size() const
122{
123 return m_channelFaces.size();
124}
125
126
127shared_ptr<UdpFace>
128UdpChannel::createFace(const shared_ptr<ip::udp::socket>& socket,
Giulio Grassi69871f02014-03-09 16:14:44 +0100129 const FaceCreatedCallback& onFaceCreated,
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700130 bool isOnDemand)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100131{
132 udp::Endpoint remoteEndpoint = socket->remote_endpoint();
133
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600134 shared_ptr<UdpFace> face;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100135
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600136 ChannelFaceMap::iterator faceMapPos = m_channelFaces.find(remoteEndpoint);
137 if (faceMapPos == m_channelFaces.end())
138 {
Alexander Afanasyeve515f0a2014-06-30 15:28:10 -0700139 face = make_shared<UdpFace>(socket, isOnDemand, m_idleFaceTimeout);
Junxiao Shic099ddb2014-12-25 20:53:20 -0700140
141 face->onFail.connectSingleShot(bind(&UdpChannel::afterFaceFailed, this, remoteEndpoint));
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600142
143 m_channelFaces[remoteEndpoint] = face;
144 }
145 else
146 {
147 // we've already created a a face for this endpoint, just reuse it
148 face = faceMapPos->second;
149
150 boost::system::error_code error;
151 socket->shutdown(ip::udp::socket::shutdown_both, error);
152 socket->close(error);
153 }
154
155 // Need to invoke the callback regardless of whether or not we have already created
156 // the face so that control responses and such can be sent.
Giulio Grassi624f6c62014-02-18 19:42:14 +0100157 onFaceCreated(face);
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600158
Giulio Grassi624f6c62014-02-18 19:42:14 +0100159 return face;
160}
161
162void
163UdpChannel::newPeer(const boost::system::error_code& error,
Junxiao Shi5dd26c32014-07-20 23:15:14 -0700164 size_t nBytesReceived)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100165{
166 NFD_LOG_DEBUG("UdpChannel::newPeer from " << m_newRemoteEndpoint);
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000167
168 shared_ptr<UdpFace> face;
169
Giulio Grassi624f6c62014-02-18 19:42:14 +0100170 ChannelFaceMap::iterator i = m_channelFaces.find(m_newRemoteEndpoint);
171 if (i != m_channelFaces.end()) {
172 //The face already exists.
173 //Usually this shouldn't happen, because the channel creates a Udpface
174 //as soon as it receives a pkt from a new endpoint and then the
175 //traffic is dispatched by the kernel directly to the face.
176 //However, if the node receives multiple packets from the same endpoint
177 //"at the same time", while the channel is creating the face the kernel
178 //could dispatch the other pkts to the channel because the face is not yet
179 //ready. In this case, the channel has to pass the pkt to the face
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000180
Giulio Grassi624f6c62014-02-18 19:42:14 +0100181 NFD_LOG_DEBUG("The creation of the face for the remote endpoint "
182 << m_newRemoteEndpoint
183 << " is in progress");
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000184
185 face = i->second;
186 }
187 else {
188 shared_ptr<ip::udp::socket> clientSocket =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700189 make_shared<ip::udp::socket>(ref(getGlobalIoService()));
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000190 clientSocket->open(m_localEndpoint.protocol());
191 clientSocket->set_option(ip::udp::socket::reuse_address(true));
192 clientSocket->bind(m_localEndpoint);
Alexander Afanasyevc5173de2014-12-18 10:51:35 -0800193 boost::system::error_code ec;
194 clientSocket->connect(m_newRemoteEndpoint, ec);
195 if (ec) {
196 NFD_LOG_WARN("Error while creating on-demand UDP face from " << m_newRemoteEndpoint << ": "
197 << boost::system::system_error(ec).what());
198 return;
199 }
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000200
Giulio Grassi69871f02014-03-09 16:14:44 +0100201 face = createFace(clientSocket,
202 onFaceCreatedNewPeerCallback,
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700203 true);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100204 }
205
Davide Pesaventoa7530582014-06-30 20:45:52 +0200206 // dispatch the datagram to the face for processing
207 face->receiveDatagram(m_inputBuffer, nBytesReceived, error);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100208
Junxiao Shi39cd6332014-11-06 21:53:18 -0700209 m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE),
Giulio Grassi624f6c62014-02-18 19:42:14 +0100210 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 Grassi624f6c62014-02-18 19:42:14 +0100224} // namespace nfd