blob: bd860ccad5b560dd63d597f1aadb0599d5ab45aa [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"
Davide Pesavento6ad890a2015-03-09 03:43:17 +010027#include "udp-face.hpp"
Giulio Grassi624f6c62014-02-18 19:42:14 +010028#include "core/global-io.hpp"
29
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{
Davide Pesavento6ad890a2015-03-09 03:43:17 +010042 setUri(FaceUri(m_localEndpoint));
Giulio Grassi624f6c62014-02-18 19:42:14 +010043
Alexander Afanasyevf6980282014-05-13 18:28:40 -070044 m_socket = make_shared<ip::udp::socket>(ref(getGlobalIoService()));
Giulio Grassi624f6c62014-02-18 19:42:14 +010045 m_socket->open(m_localEndpoint.protocol());
Davide Pesavento6ad890a2015-03-09 03:43:17 +010046 m_socket->set_option(ip::udp::socket::reuse_address(true));
Alexander Afanasyeveee71fd2014-03-13 17:40:33 -070047 if (m_localEndpoint.address().is_v6())
Davide Pesavento6ad890a2015-03-09 03:43:17 +010048 m_socket->set_option(ip::v6_only(true));
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060049
Giulio Grassi624f6c62014-02-18 19:42:14 +010050 try {
51 m_socket->bind(m_localEndpoint);
52 }
Davide Pesavento6ad890a2015-03-09 03:43:17 +010053 catch (const boost::system::system_error& e) {
54 // bind failed, so the socket is useless now
Giulio Grassi624f6c62014-02-18 19:42:14 +010055 m_socket->close();
Davide Pesavento6ad890a2015-03-09 03:43:17 +010056 throw Error("bind failed: " + std::string(e.what()));
Giulio Grassi624f6c62014-02-18 19:42:14 +010057 }
Giulio Grassi624f6c62014-02-18 19:42:14 +010058}
59
60void
61UdpChannel::listen(const FaceCreatedCallback& onFaceCreated,
Davide Pesavento6ad890a2015-03-09 03:43:17 +010062 const ConnectFailedCallback& onReceiveFailed)
Giulio Grassi624f6c62014-02-18 19:42:14 +010063{
Davide Pesavento6ad890a2015-03-09 03:43:17 +010064 if (isListening()) {
65 NFD_LOG_WARN("[" << m_localEndpoint << "] Already listening");
66 return;
Giulio Grassi624f6c62014-02-18 19:42:14 +010067 }
68 m_isListening = true;
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060069
Junxiao Shi39cd6332014-11-06 21:53:18 -070070 m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE),
Giulio Grassi624f6c62014-02-18 19:42:14 +010071 m_newRemoteEndpoint,
Davide Pesavento6ad890a2015-03-09 03:43:17 +010072 bind(&UdpChannel::handleNewPeer, this,
Giulio Grassi624f6c62014-02-18 19:42:14 +010073 boost::asio::placeholders::error,
Davide Pesavento6ad890a2015-03-09 03:43:17 +010074 boost::asio::placeholders::bytes_transferred,
75 onFaceCreated, onReceiveFailed));
Giulio Grassi624f6c62014-02-18 19:42:14 +010076}
77
Giulio Grassi624f6c62014-02-18 19:42:14 +010078void
79UdpChannel::connect(const udp::Endpoint& remoteEndpoint,
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060080 const FaceCreatedCallback& onFaceCreated,
81 const ConnectFailedCallback& onConnectFailed)
Giulio Grassi624f6c62014-02-18 19:42:14 +010082{
Davide Pesavento6ad890a2015-03-09 03:43:17 +010083 auto it = m_channelFaces.find(remoteEndpoint);
84 if (it != m_channelFaces.end()) {
85 it->second->setOnDemand(false);
86 onFaceCreated(it->second);
Giulio Grassi624f6c62014-02-18 19:42:14 +010087 return;
88 }
89
Davide Pesavento6ad890a2015-03-09 03:43:17 +010090 // creating a new socket for the face that will be created soon
Giulio Grassi624f6c62014-02-18 19:42:14 +010091 shared_ptr<ip::udp::socket> clientSocket =
Alexander Afanasyevf6980282014-05-13 18:28:40 -070092 make_shared<ip::udp::socket>(ref(getGlobalIoService()));
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060093
Giulio Grassi624f6c62014-02-18 19:42:14 +010094 clientSocket->open(m_localEndpoint.protocol());
95 clientSocket->set_option(ip::udp::socket::reuse_address(true));
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060096
Giulio Grassi624f6c62014-02-18 19:42:14 +010097 try {
98 clientSocket->bind(m_localEndpoint);
99 clientSocket->connect(remoteEndpoint); //@todo connect or async_connect
100 //(since there is no handshake the connect shouldn't block). If we go for
101 //async_connect, make sure that if in the meantime we receive a UDP pkt from
102 //that endpoint nothing bad happen (it's difficult, but it could happen)
103 }
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100104 catch (const boost::system::system_error& e) {
Giulio Grassi624f6c62014-02-18 19:42:14 +0100105 clientSocket->close();
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600106 onConnectFailed("Failed to configure socket (" + std::string(e.what()) + ")");
107 return;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100108 }
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100109
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700110 createFace(clientSocket, onFaceCreated, false);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100111}
112
Giulio Grassi624f6c62014-02-18 19:42:14 +0100113size_t
114UdpChannel::size() const
115{
116 return m_channelFaces.size();
117}
118
Giulio Grassi624f6c62014-02-18 19:42:14 +0100119shared_ptr<UdpFace>
120UdpChannel::createFace(const shared_ptr<ip::udp::socket>& socket,
Giulio Grassi69871f02014-03-09 16:14:44 +0100121 const FaceCreatedCallback& onFaceCreated,
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700122 bool isOnDemand)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100123{
124 udp::Endpoint remoteEndpoint = socket->remote_endpoint();
125
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600126 shared_ptr<UdpFace> face;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100127
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100128 auto it = m_channelFaces.find(remoteEndpoint);
129 if (it == m_channelFaces.end())
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600130 {
Alexander Afanasyeve515f0a2014-06-30 15:28:10 -0700131 face = make_shared<UdpFace>(socket, isOnDemand, m_idleFaceTimeout);
Junxiao Shic099ddb2014-12-25 20:53:20 -0700132
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100133 face->onFail.connectSingleShot([this, remoteEndpoint] (const std::string&) {
134 NFD_LOG_TRACE("Erasing " << remoteEndpoint << " from channel face map");
135 m_channelFaces.erase(remoteEndpoint);
136 });
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600137
138 m_channelFaces[remoteEndpoint] = face;
139 }
140 else
141 {
142 // we've already created a a face for this endpoint, just reuse it
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100143 face = it->second;
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600144
145 boost::system::error_code error;
146 socket->shutdown(ip::udp::socket::shutdown_both, error);
147 socket->close(error);
148 }
149
150 // Need to invoke the callback regardless of whether or not we have already created
151 // the face so that control responses and such can be sent.
Giulio Grassi624f6c62014-02-18 19:42:14 +0100152 onFaceCreated(face);
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600153
Giulio Grassi624f6c62014-02-18 19:42:14 +0100154 return face;
155}
156
157void
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100158UdpChannel::handleNewPeer(const boost::system::error_code& error,
159 size_t nBytesReceived,
160 const FaceCreatedCallback& onFaceCreated,
161 const ConnectFailedCallback& onReceiveFailed)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100162{
Alexander Afanasyev90c20fa2015-02-12 16:46:45 -0800163 if (error) {
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100164 if (error == boost::asio::error::operation_aborted) // when the socket is closed by someone
Alexander Afanasyev90c20fa2015-02-12 16:46:45 -0800165 return;
166
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100167 NFD_LOG_DEBUG("[" << m_localEndpoint << "] Receive failed: " << error.message());
168 if (onReceiveFailed)
169 onReceiveFailed(error.message());
Alexander Afanasyev90c20fa2015-02-12 16:46:45 -0800170 return;
171 }
172
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100173 NFD_LOG_DEBUG("[" << m_localEndpoint << "] New peer " << m_newRemoteEndpoint);
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000174
175 shared_ptr<UdpFace> face;
176
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100177 auto it = m_channelFaces.find(m_newRemoteEndpoint);
178 if (it != m_channelFaces.end()) {
Giulio Grassi624f6c62014-02-18 19:42:14 +0100179 //The face already exists.
180 //Usually this shouldn't happen, because the channel creates a Udpface
181 //as soon as it receives a pkt from a new endpoint and then the
182 //traffic is dispatched by the kernel directly to the face.
183 //However, if the node receives multiple packets from the same endpoint
184 //"at the same time", while the channel is creating the face the kernel
185 //could dispatch the other pkts to the channel because the face is not yet
186 //ready. In this case, the channel has to pass the pkt to the face
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000187
Giulio Grassi624f6c62014-02-18 19:42:14 +0100188 NFD_LOG_DEBUG("The creation of the face for the remote endpoint "
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100189 << m_newRemoteEndpoint << " is already in progress");
190 face = it->second;
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000191 }
192 else {
193 shared_ptr<ip::udp::socket> clientSocket =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700194 make_shared<ip::udp::socket>(ref(getGlobalIoService()));
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000195 clientSocket->open(m_localEndpoint.protocol());
196 clientSocket->set_option(ip::udp::socket::reuse_address(true));
197 clientSocket->bind(m_localEndpoint);
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100198
Alexander Afanasyevc5173de2014-12-18 10:51:35 -0800199 boost::system::error_code ec;
200 clientSocket->connect(m_newRemoteEndpoint, ec);
201 if (ec) {
202 NFD_LOG_WARN("Error while creating on-demand UDP face from " << m_newRemoteEndpoint << ": "
203 << boost::system::system_error(ec).what());
204 return;
205 }
Alexander Afanasyev6f5ff632014-03-07 16:40:10 +0000206
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100207 face = createFace(clientSocket, onFaceCreated, true);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100208 }
209
Davide Pesaventoa7530582014-06-30 20:45:52 +0200210 // dispatch the datagram to the face for processing
211 face->receiveDatagram(m_inputBuffer, nBytesReceived, error);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100212
Junxiao Shi39cd6332014-11-06 21:53:18 -0700213 m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE),
Giulio Grassi624f6c62014-02-18 19:42:14 +0100214 m_newRemoteEndpoint,
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100215 bind(&UdpChannel::handleNewPeer, this,
Giulio Grassi624f6c62014-02-18 19:42:14 +0100216 boost::asio::placeholders::error,
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100217 boost::asio::placeholders::bytes_transferred,
218 onFaceCreated, onReceiveFailed));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100219}
220
Giulio Grassi624f6c62014-02-18 19:42:14 +0100221} // namespace nfd