blob: cc60a63cff5675f06e3794842a63ee53ead98bcd [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/>.
Steve DiBenedettoef04f272014-06-04 14:28:31 -060024 */
Giulio Grassi624f6c62014-02-18 19:42:14 +010025
26#include "udp-factory.hpp"
Yukai Tu0a49d342015-09-13 12:54:22 +080027#include "generic-link-service.hpp"
Yukai Tu0a49d342015-09-13 12:54:22 +080028#include "multicast-udp-transport.hpp"
Giulio Grassi624f6c62014-02-18 19:42:14 +010029#include "core/global-io.hpp"
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060030#include "core/network-interface.hpp"
Giulio Grassi624f6c62014-02-18 19:42:14 +010031
Davide Pesavento292e5e12015-03-13 02:08:33 +010032#ifdef __linux__
33#include <cerrno> // for errno
34#include <cstring> // for std::strerror()
35#include <sys/socket.h> // for setsockopt()
Giulio Grassi6d7176d2014-04-16 16:08:48 +020036#endif
37
Giulio Grassi624f6c62014-02-18 19:42:14 +010038namespace nfd {
39
Davide Pesavento1d7e7af2015-10-10 23:54:08 +020040namespace ip = boost::asio::ip;
Junxiao Shi79494162014-04-02 18:25:11 -070041
Giulio Grassi624f6c62014-02-18 19:42:14 +010042NFD_LOG_INIT("UdpFactory");
43
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060044void
45UdpFactory::prohibitEndpoint(const udp::Endpoint& endpoint)
46{
Davide Pesavento1d7e7af2015-10-10 23:54:08 +020047 if (endpoint.address().is_v4() &&
48 endpoint.address() == ip::address_v4::any()) {
49 prohibitAllIpv4Endpoints(endpoint.port());
50 }
51 else if (endpoint.address().is_v6() &&
52 endpoint.address() == ip::address_v6::any()) {
53 prohibitAllIpv6Endpoints(endpoint.port());
54 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060055
Alexander Afanasyev70aaf8a2014-12-13 00:44:22 -080056 NFD_LOG_TRACE("prohibiting UDP " << endpoint);
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060057 m_prohibitedEndpoints.insert(endpoint);
58}
59
60void
Davide Pesavento1d7e7af2015-10-10 23:54:08 +020061UdpFactory::prohibitAllIpv4Endpoints(uint16_t port)
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060062{
Davide Pesaventob499a602014-11-18 22:36:56 +010063 for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
Davide Pesavento1d7e7af2015-10-10 23:54:08 +020064 for (const auto& addr : nic.ipv4Addresses) {
65 if (addr != ip::address_v4::any()) {
Alexander Afanasyev70aaf8a2014-12-13 00:44:22 -080066 prohibitEndpoint(udp::Endpoint(addr, port));
67 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060068 }
69
Davide Pesavento1d7e7af2015-10-10 23:54:08 +020070 if (nic.isBroadcastCapable() &&
71 nic.broadcastAddress != ip::address_v4::any()) {
Davide Pesaventob499a602014-11-18 22:36:56 +010072 prohibitEndpoint(udp::Endpoint(nic.broadcastAddress, port));
73 }
74 }
75
Davide Pesavento1d7e7af2015-10-10 23:54:08 +020076 prohibitEndpoint(udp::Endpoint(ip::address_v4::broadcast(), port));
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060077}
78
79void
Davide Pesavento1d7e7af2015-10-10 23:54:08 +020080UdpFactory::prohibitAllIpv6Endpoints(uint16_t port)
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060081{
Davide Pesaventob499a602014-11-18 22:36:56 +010082 for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
Davide Pesavento1d7e7af2015-10-10 23:54:08 +020083 for (const auto& addr : nic.ipv6Addresses) {
84 if (addr != ip::address_v6::any()) {
Alexander Afanasyev70aaf8a2014-12-13 00:44:22 -080085 prohibitEndpoint(udp::Endpoint(addr, port));
86 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060087 }
Davide Pesaventob499a602014-11-18 22:36:56 +010088 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060089}
90
Giulio Grassi624f6c62014-02-18 19:42:14 +010091shared_ptr<UdpChannel>
92UdpFactory::createChannel(const udp::Endpoint& endpoint,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070093 const time::seconds& timeout)
Giulio Grassi624f6c62014-02-18 19:42:14 +010094{
Davide Pesaventob499a602014-11-18 22:36:56 +010095 NFD_LOG_DEBUG("Creating unicast channel " << endpoint);
Junxiao Shi79494162014-04-02 18:25:11 -070096
Davide Pesavento1d7e7af2015-10-10 23:54:08 +020097 auto channel = findChannel(endpoint);
98 if (channel)
Giulio Grassi624f6c62014-02-18 19:42:14 +010099 return channel;
100
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200101 if (endpoint.address().is_multicast()) {
102 BOOST_THROW_EXCEPTION(Error("createChannel is only for unicast channels. The provided endpoint "
103 "is multicast. Use createMulticastFace to create a multicast face"));
104 }
105
Yukai Tu0a49d342015-09-13 12:54:22 +0800106 // check if the endpoint is already used by a multicast face
107 auto face = findMulticastFace(endpoint);
108 if (face) {
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700109 BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP unicast channel, local "
110 "endpoint is already allocated for a UDP multicast face"));
Yukai Tu0a49d342015-09-13 12:54:22 +0800111 }
Junxiao Shi79494162014-04-02 18:25:11 -0700112
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700113 channel = make_shared<UdpChannel>(endpoint, timeout);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100114 m_channels[endpoint] = channel;
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600115 prohibitEndpoint(endpoint);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100116
117 return channel;
118}
119
120shared_ptr<UdpChannel>
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200121UdpFactory::createChannel(const std::string& localIp, const std::string& localPort,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700122 const time::seconds& timeout)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100123{
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200124 udp::Endpoint endpoint(ip::address::from_string(localIp),
125 boost::lexical_cast<uint16_t>(localPort));
Alexander Afanasyev0e156df2015-01-26 22:33:43 -0800126 return createChannel(endpoint, timeout);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100127}
128
Junxiao Shicde37ad2015-12-24 01:02:05 -0700129shared_ptr<Face>
Giulio Grassi624f6c62014-02-18 19:42:14 +0100130UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint,
Giulio Grassi6d7176d2014-04-16 16:08:48 +0200131 const udp::Endpoint& multicastEndpoint,
Davide Pesavento292e5e12015-03-13 02:08:33 +0100132 const std::string& networkInterfaceName/* = ""*/)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100133{
Davide Pesavento292e5e12015-03-13 02:08:33 +0100134 // checking if the local and multicast endpoints are already in use for a multicast face
Yukai Tu0a49d342015-09-13 12:54:22 +0800135 auto face = findMulticastFace(localEndpoint);
136 if (face) {
137 if (face->getRemoteUri() == FaceUri(multicastEndpoint))
Davide Pesavento292e5e12015-03-13 02:08:33 +0100138 return face;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100139 else
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700140 BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, local "
141 "endpoint is already allocated for a UDP multicast face "
142 "on a different multicast group"));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100143 }
Junxiao Shi79494162014-04-02 18:25:11 -0700144
Davide Pesavento292e5e12015-03-13 02:08:33 +0100145 // checking if the local endpoint is already in use for a unicast channel
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200146 auto unicastCh = findChannel(localEndpoint);
147 if (unicastCh) {
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700148 BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, local "
149 "endpoint is already allocated for a UDP unicast channel"));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100150 }
151
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600152 if (m_prohibitedEndpoints.find(multicastEndpoint) != m_prohibitedEndpoints.end()) {
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700153 BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, "
154 "remote endpoint is owned by this NFD instance"));
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600155 }
156
Giulio Grassi624f6c62014-02-18 19:42:14 +0100157 if (localEndpoint.address().is_v6() || multicastEndpoint.address().is_v6()) {
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700158 BOOST_THROW_EXCEPTION(Error("IPv6 multicast is not supported yet. Please provide an IPv4 "
159 "address"));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100160 }
Junxiao Shi79494162014-04-02 18:25:11 -0700161
Giulio Grassi624f6c62014-02-18 19:42:14 +0100162 if (localEndpoint.port() != multicastEndpoint.port()) {
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700163 BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, "
164 "both endpoints should have the same port number. "));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100165 }
166
167 if (!multicastEndpoint.address().is_multicast()) {
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700168 BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, "
169 "the multicast group given as input is not a multicast address"));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100170 }
171
Davide Pesavento292e5e12015-03-13 02:08:33 +0100172 ip::udp::socket receiveSocket(getGlobalIoService());
173 receiveSocket.open(multicastEndpoint.protocol());
174 receiveSocket.set_option(ip::udp::socket::reuse_address(true));
175 receiveSocket.bind(multicastEndpoint);
Junxiao Shi79494162014-04-02 18:25:11 -0700176
Davide Pesavento292e5e12015-03-13 02:08:33 +0100177 ip::udp::socket sendSocket(getGlobalIoService());
178 sendSocket.open(multicastEndpoint.protocol());
179 sendSocket.set_option(ip::udp::socket::reuse_address(true));
180 sendSocket.set_option(ip::multicast::enable_loopback(false));
181 sendSocket.bind(udp::Endpoint(ip::address_v4::any(), multicastEndpoint.port()));
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200182 if (localEndpoint.address() != ip::address_v4::any())
Davide Pesavento292e5e12015-03-13 02:08:33 +0100183 sendSocket.set_option(ip::multicast::outbound_interface(localEndpoint.address().to_v4()));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100184
Davide Pesavento292e5e12015-03-13 02:08:33 +0100185 sendSocket.set_option(ip::multicast::join_group(multicastEndpoint.address().to_v4(),
186 localEndpoint.address().to_v4()));
187 receiveSocket.set_option(ip::multicast::join_group(multicastEndpoint.address().to_v4(),
Alexander Afanasyevdc017002014-06-18 16:37:54 -0700188 localEndpoint.address().to_v4()));
189
Davide Pesavento292e5e12015-03-13 02:08:33 +0100190#ifdef __linux__
191 /*
192 * On Linux, if there is more than one MulticastUdpFace for the same multicast
193 * group but they are bound to different network interfaces, the socket needs
194 * to be bound to the specific interface using SO_BINDTODEVICE, otherwise the
195 * face will receive all packets sent to the other interfaces as well.
196 * This happens only on Linux. On OS X, the ip::multicast::join_group option
197 * is enough to get the desired behaviour.
198 */
Giulio Grassi6d7176d2014-04-16 16:08:48 +0200199 if (!networkInterfaceName.empty()) {
Davide Pesavento292e5e12015-03-13 02:08:33 +0100200 if (::setsockopt(receiveSocket.native_handle(), SOL_SOCKET, SO_BINDTODEVICE,
201 networkInterfaceName.c_str(), networkInterfaceName.size() + 1) < 0) {
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700202 BOOST_THROW_EXCEPTION(Error("Cannot bind multicast face to " + networkInterfaceName +
203 ": " + std::strerror(errno)));
Giulio Grassi6d7176d2014-04-16 16:08:48 +0200204 }
205 }
Giulio Grassi6d7176d2014-04-16 16:08:48 +0200206#endif
207
Yukai Tu0a49d342015-09-13 12:54:22 +0800208 auto linkService = make_unique<face::GenericLinkService>();
209 auto transport = make_unique<face::MulticastUdpTransport>(localEndpoint, multicastEndpoint,
210 std::move(receiveSocket),
211 std::move(sendSocket));
Junxiao Shicde37ad2015-12-24 01:02:05 -0700212 face = make_shared<Face>(std::move(linkService), std::move(transport));
Junxiao Shic099ddb2014-12-25 20:53:20 -0700213
Davide Pesavento292e5e12015-03-13 02:08:33 +0100214 m_multicastFaces[localEndpoint] = face;
Junxiao Shicde37ad2015-12-24 01:02:05 -0700215 connectFaceClosedSignal(*face, [this, localEndpoint] { m_multicastFaces.erase(localEndpoint); });
Giulio Grassi624f6c62014-02-18 19:42:14 +0100216
Davide Pesavento292e5e12015-03-13 02:08:33 +0100217 return face;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100218}
219
Junxiao Shicde37ad2015-12-24 01:02:05 -0700220shared_ptr<Face>
Giulio Grassi624f6c62014-02-18 19:42:14 +0100221UdpFactory::createMulticastFace(const std::string& localIp,
222 const std::string& multicastIp,
Giulio Grassi6d7176d2014-04-16 16:08:48 +0200223 const std::string& multicastPort,
Davide Pesavento292e5e12015-03-13 02:08:33 +0100224 const std::string& networkInterfaceName/* = ""*/)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100225{
Davide Pesavento292e5e12015-03-13 02:08:33 +0100226 udp::Endpoint localEndpoint(ip::address::from_string(localIp),
Chengyu Fan4381fb62015-01-14 11:37:04 -0700227 boost::lexical_cast<uint16_t>(multicastPort));
Davide Pesavento292e5e12015-03-13 02:08:33 +0100228 udp::Endpoint multicastEndpoint(ip::address::from_string(multicastIp),
Chengyu Fan4381fb62015-01-14 11:37:04 -0700229 boost::lexical_cast<uint16_t>(multicastPort));
Chengyu Fan4381fb62015-01-14 11:37:04 -0700230 return createMulticastFace(localEndpoint, multicastEndpoint, networkInterfaceName);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100231}
232
233void
234UdpFactory::createFace(const FaceUri& uri,
Yukai Tu7c90e6d2015-07-11 12:21:46 +0800235 ndn::nfd::FacePersistency persistency,
Giulio Grassi624f6c62014-02-18 19:42:14 +0100236 const FaceCreatedCallback& onCreated,
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200237 const FaceCreationFailedCallback& onConnectFailed)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100238{
Chengyu Fan4381fb62015-01-14 11:37:04 -0700239 BOOST_ASSERT(uri.isCanonical());
Davide Pesavento292e5e12015-03-13 02:08:33 +0100240
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200241 if (persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
242 BOOST_THROW_EXCEPTION(Error("UdpFactory::createFace does not support FACE_PERSISTENCY_ON_DEMAND"));
243 }
244
245 udp::Endpoint endpoint(ip::address::from_string(uri.getHost()),
246 boost::lexical_cast<uint16_t>(uri.getPort()));
Junxiao Shi79494162014-04-02 18:25:11 -0700247
Giulio Grassi624f6c62014-02-18 19:42:14 +0100248 if (endpoint.address().is_multicast()) {
249 onConnectFailed("The provided address is multicast. Please use createMulticastFace method");
250 return;
251 }
Junxiao Shi79494162014-04-02 18:25:11 -0700252
Davide Pesavento292e5e12015-03-13 02:08:33 +0100253 if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end()) {
254 onConnectFailed("Requested endpoint is prohibited "
255 "(reserved by this NFD or disallowed by face management protocol)");
256 return;
257 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600258
Giulio Grassi624f6c62014-02-18 19:42:14 +0100259 // very simple logic for now
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200260 for (const auto& i : m_channels) {
261 if ((i.first.address().is_v4() && endpoint.address().is_v4()) ||
262 (i.first.address().is_v6() && endpoint.address().is_v6())) {
263 i.second->connect(endpoint, persistency, onCreated, onConnectFailed);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100264 return;
265 }
266 }
Davide Pesavento292e5e12015-03-13 02:08:33 +0100267
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200268 onConnectFailed("No channels available to connect to " + boost::lexical_cast<std::string>(endpoint));
269}
270
271std::vector<shared_ptr<const Channel>>
272UdpFactory::getChannels() const
273{
274 std::vector<shared_ptr<const Channel>> channels;
275 channels.reserve(m_channels.size());
276
277 for (const auto& i : m_channels)
278 channels.push_back(i.second);
279
280 return channels;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100281}
282
283shared_ptr<UdpChannel>
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200284UdpFactory::findChannel(const udp::Endpoint& localEndpoint) const
Giulio Grassi624f6c62014-02-18 19:42:14 +0100285{
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200286 auto i = m_channels.find(localEndpoint);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100287 if (i != m_channels.end())
288 return i->second;
289 else
Yukai Tu0a49d342015-09-13 12:54:22 +0800290 return nullptr;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100291}
292
Junxiao Shicde37ad2015-12-24 01:02:05 -0700293shared_ptr<Face>
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200294UdpFactory::findMulticastFace(const udp::Endpoint& localEndpoint) const
Steve DiBenedettoef04f272014-06-04 14:28:31 -0600295{
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200296 auto i = m_multicastFaces.find(localEndpoint);
297 if (i != m_multicastFaces.end())
298 return i->second;
299 else
300 return nullptr;
Steve DiBenedettoef04f272014-06-04 14:28:31 -0600301}
302
Giulio Grassi624f6c62014-02-18 19:42:14 +0100303} // namespace nfd