blob: 74636d85a11bcc44f4c2f0cb9cb93ef0f7a760f3 [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"
28#include "lp-face-wrapper.hpp"
29#include "multicast-udp-transport.hpp"
Giulio Grassi624f6c62014-02-18 19:42:14 +010030#include "core/global-io.hpp"
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060031#include "core/network-interface.hpp"
Giulio Grassi624f6c62014-02-18 19:42:14 +010032
Davide Pesavento292e5e12015-03-13 02:08:33 +010033#ifdef __linux__
34#include <cerrno> // for errno
35#include <cstring> // for std::strerror()
36#include <sys/socket.h> // for setsockopt()
Giulio Grassi6d7176d2014-04-16 16:08:48 +020037#endif
38
Giulio Grassi624f6c62014-02-18 19:42:14 +010039namespace nfd {
40
41using namespace boost::asio;
Junxiao Shi79494162014-04-02 18:25:11 -070042
Giulio Grassi624f6c62014-02-18 19:42:14 +010043NFD_LOG_INIT("UdpFactory");
44
Alexander Afanasyev70aaf8a2014-12-13 00:44:22 -080045static const boost::asio::ip::address_v4 ALL_V4_ENDPOINT(
46 boost::asio::ip::address_v4::from_string("0.0.0.0"));
47
48static const boost::asio::ip::address_v6 ALL_V6_ENDPOINT(
49 boost::asio::ip::address_v6::from_string("::"));
50
Giulio Grassi624f6c62014-02-18 19:42:14 +010051UdpFactory::UdpFactory(const std::string& defaultPort/* = "6363"*/)
52 : m_defaultPort(defaultPort)
53{
54}
55
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060056void
57UdpFactory::prohibitEndpoint(const udp::Endpoint& endpoint)
58{
59 using namespace boost::asio::ip;
60
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060061 const address& address = endpoint.address();
62
63 if (address.is_v4() && address == ALL_V4_ENDPOINT)
64 {
65 prohibitAllIpv4Endpoints(endpoint.port());
66 }
67 else if (endpoint.address().is_v6() && address == ALL_V6_ENDPOINT)
68 {
69 prohibitAllIpv6Endpoints(endpoint.port());
70 }
71
Alexander Afanasyev70aaf8a2014-12-13 00:44:22 -080072 NFD_LOG_TRACE("prohibiting UDP " << endpoint);
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060073
74 m_prohibitedEndpoints.insert(endpoint);
75}
76
77void
78UdpFactory::prohibitAllIpv4Endpoints(const uint16_t port)
79{
80 using namespace boost::asio::ip;
81
Davide Pesaventob499a602014-11-18 22:36:56 +010082 for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
83 for (const address_v4& addr : nic.ipv4Addresses) {
Alexander Afanasyev70aaf8a2014-12-13 00:44:22 -080084 if (addr != ALL_V4_ENDPOINT) {
85 prohibitEndpoint(udp::Endpoint(addr, port));
86 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060087 }
88
Alexander Afanasyev70aaf8a2014-12-13 00:44:22 -080089 if (nic.isBroadcastCapable() && nic.broadcastAddress != ALL_V4_ENDPOINT)
Davide Pesaventob499a602014-11-18 22:36:56 +010090 {
91 NFD_LOG_TRACE("prohibiting broadcast address: " << nic.broadcastAddress.to_string());
92 prohibitEndpoint(udp::Endpoint(nic.broadcastAddress, port));
93 }
94 }
95
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060096 prohibitEndpoint(udp::Endpoint(address::from_string("255.255.255.255"), port));
97}
98
99void
100UdpFactory::prohibitAllIpv6Endpoints(const uint16_t port)
101{
102 using namespace boost::asio::ip;
103
Davide Pesaventob499a602014-11-18 22:36:56 +0100104 for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
105 for (const address_v6& addr : nic.ipv6Addresses) {
Alexander Afanasyev70aaf8a2014-12-13 00:44:22 -0800106 if (addr != ALL_V6_ENDPOINT) {
107 prohibitEndpoint(udp::Endpoint(addr, port));
108 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600109 }
Davide Pesaventob499a602014-11-18 22:36:56 +0100110 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600111}
112
Giulio Grassi624f6c62014-02-18 19:42:14 +0100113shared_ptr<UdpChannel>
114UdpFactory::createChannel(const udp::Endpoint& endpoint,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700115 const time::seconds& timeout)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100116{
Davide Pesaventob499a602014-11-18 22:36:56 +0100117 NFD_LOG_DEBUG("Creating unicast channel " << endpoint);
Junxiao Shi79494162014-04-02 18:25:11 -0700118
Giulio Grassi624f6c62014-02-18 19:42:14 +0100119 shared_ptr<UdpChannel> channel = findChannel(endpoint);
120 if (static_cast<bool>(channel))
121 return channel;
122
Yukai Tu0a49d342015-09-13 12:54:22 +0800123 // check if the endpoint is already used by a multicast face
124 auto face = findMulticastFace(endpoint);
125 if (face) {
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700126 BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP unicast channel, local "
127 "endpoint is already allocated for a UDP multicast face"));
Yukai Tu0a49d342015-09-13 12:54:22 +0800128 }
Junxiao Shi79494162014-04-02 18:25:11 -0700129
Giulio Grassi624f6c62014-02-18 19:42:14 +0100130 if (endpoint.address().is_multicast()) {
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700131 BOOST_THROW_EXCEPTION(Error("This method is only for unicast channel. The provided "
132 "endpoint is multicast. Use createMulticastFace to "
133 "create a multicast face"));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100134 }
135
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700136 channel = make_shared<UdpChannel>(endpoint, timeout);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100137 m_channels[endpoint] = channel;
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600138 prohibitEndpoint(endpoint);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100139
140 return channel;
141}
142
143shared_ptr<UdpChannel>
Alexander Afanasyev0e156df2015-01-26 22:33:43 -0800144UdpFactory::createChannel(const std::string& localIp,
Giulio Grassi624f6c62014-02-18 19:42:14 +0100145 const std::string& localPort,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700146 const time::seconds& timeout)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100147{
Alexander Afanasyev0e156df2015-01-26 22:33:43 -0800148 using namespace boost::asio::ip;
149 udp::Endpoint endpoint(address::from_string(localIp), boost::lexical_cast<uint16_t>(localPort));
150 return createChannel(endpoint, timeout);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100151}
152
Yukai Tu0a49d342015-09-13 12:54:22 +0800153shared_ptr<face::LpFaceWrapper>
Giulio Grassi624f6c62014-02-18 19:42:14 +0100154UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint,
Giulio Grassi6d7176d2014-04-16 16:08:48 +0200155 const udp::Endpoint& multicastEndpoint,
Davide Pesavento292e5e12015-03-13 02:08:33 +0100156 const std::string& networkInterfaceName/* = ""*/)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100157{
Davide Pesavento292e5e12015-03-13 02:08:33 +0100158 // checking if the local and multicast endpoints are already in use for a multicast face
Yukai Tu0a49d342015-09-13 12:54:22 +0800159 auto face = findMulticastFace(localEndpoint);
160 if (face) {
161 if (face->getRemoteUri() == FaceUri(multicastEndpoint))
Davide Pesavento292e5e12015-03-13 02:08:33 +0100162 return face;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100163 else
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700164 BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, local "
165 "endpoint is already allocated for a UDP multicast face "
166 "on a different multicast group"));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100167 }
Junxiao Shi79494162014-04-02 18:25:11 -0700168
Davide Pesavento292e5e12015-03-13 02:08:33 +0100169 // checking if the local endpoint is already in use for a unicast channel
Giulio Grassi624f6c62014-02-18 19:42:14 +0100170 shared_ptr<UdpChannel> unicast = findChannel(localEndpoint);
171 if (static_cast<bool>(unicast)) {
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700172 BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, local "
173 "endpoint is already allocated for a UDP unicast channel"));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100174 }
175
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600176 if (m_prohibitedEndpoints.find(multicastEndpoint) != m_prohibitedEndpoints.end()) {
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700177 BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, "
178 "remote endpoint is owned by this NFD instance"));
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600179 }
180
Giulio Grassi624f6c62014-02-18 19:42:14 +0100181 if (localEndpoint.address().is_v6() || multicastEndpoint.address().is_v6()) {
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700182 BOOST_THROW_EXCEPTION(Error("IPv6 multicast is not supported yet. Please provide an IPv4 "
183 "address"));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100184 }
Junxiao Shi79494162014-04-02 18:25:11 -0700185
Giulio Grassi624f6c62014-02-18 19:42:14 +0100186 if (localEndpoint.port() != multicastEndpoint.port()) {
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700187 BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, "
188 "both endpoints should have the same port number. "));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100189 }
190
191 if (!multicastEndpoint.address().is_multicast()) {
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700192 BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, "
193 "the multicast group given as input is not a multicast address"));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100194 }
195
Davide Pesavento292e5e12015-03-13 02:08:33 +0100196 ip::udp::socket receiveSocket(getGlobalIoService());
197 receiveSocket.open(multicastEndpoint.protocol());
198 receiveSocket.set_option(ip::udp::socket::reuse_address(true));
199 receiveSocket.bind(multicastEndpoint);
Junxiao Shi79494162014-04-02 18:25:11 -0700200
Davide Pesavento292e5e12015-03-13 02:08:33 +0100201 ip::udp::socket sendSocket(getGlobalIoService());
202 sendSocket.open(multicastEndpoint.protocol());
203 sendSocket.set_option(ip::udp::socket::reuse_address(true));
204 sendSocket.set_option(ip::multicast::enable_loopback(false));
205 sendSocket.bind(udp::Endpoint(ip::address_v4::any(), multicastEndpoint.port()));
206 if (localEndpoint.address() != ALL_V4_ENDPOINT)
207 sendSocket.set_option(ip::multicast::outbound_interface(localEndpoint.address().to_v4()));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100208
Davide Pesavento292e5e12015-03-13 02:08:33 +0100209 sendSocket.set_option(ip::multicast::join_group(multicastEndpoint.address().to_v4(),
210 localEndpoint.address().to_v4()));
211 receiveSocket.set_option(ip::multicast::join_group(multicastEndpoint.address().to_v4(),
Alexander Afanasyevdc017002014-06-18 16:37:54 -0700212 localEndpoint.address().to_v4()));
213
Davide Pesavento292e5e12015-03-13 02:08:33 +0100214#ifdef __linux__
215 /*
216 * On Linux, if there is more than one MulticastUdpFace for the same multicast
217 * group but they are bound to different network interfaces, the socket needs
218 * to be bound to the specific interface using SO_BINDTODEVICE, otherwise the
219 * face will receive all packets sent to the other interfaces as well.
220 * This happens only on Linux. On OS X, the ip::multicast::join_group option
221 * is enough to get the desired behaviour.
222 */
Giulio Grassi6d7176d2014-04-16 16:08:48 +0200223 if (!networkInterfaceName.empty()) {
Davide Pesavento292e5e12015-03-13 02:08:33 +0100224 if (::setsockopt(receiveSocket.native_handle(), SOL_SOCKET, SO_BINDTODEVICE,
225 networkInterfaceName.c_str(), networkInterfaceName.size() + 1) < 0) {
Spyridon Mastorakis149e02c2015-07-27 13:22:22 -0700226 BOOST_THROW_EXCEPTION(Error("Cannot bind multicast face to " + networkInterfaceName +
227 ": " + std::strerror(errno)));
Giulio Grassi6d7176d2014-04-16 16:08:48 +0200228 }
229 }
Giulio Grassi6d7176d2014-04-16 16:08:48 +0200230#endif
231
Yukai Tu0a49d342015-09-13 12:54:22 +0800232 auto linkService = make_unique<face::GenericLinkService>();
233 auto transport = make_unique<face::MulticastUdpTransport>(localEndpoint, multicastEndpoint,
234 std::move(receiveSocket),
235 std::move(sendSocket));
236 auto lpFace = make_unique<face::LpFace>(std::move(linkService), std::move(transport));
237 face = make_shared<face::LpFaceWrapper>(std::move(lpFace));
Junxiao Shic099ddb2014-12-25 20:53:20 -0700238
Davide Pesavento292e5e12015-03-13 02:08:33 +0100239 face->onFail.connectSingleShot([this, localEndpoint] (const std::string& reason) {
240 m_multicastFaces.erase(localEndpoint);
241 });
242 m_multicastFaces[localEndpoint] = face;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100243
Davide Pesavento292e5e12015-03-13 02:08:33 +0100244 return face;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100245}
246
Yukai Tu0a49d342015-09-13 12:54:22 +0800247shared_ptr<face::LpFaceWrapper>
Giulio Grassi624f6c62014-02-18 19:42:14 +0100248UdpFactory::createMulticastFace(const std::string& localIp,
249 const std::string& multicastIp,
Giulio Grassi6d7176d2014-04-16 16:08:48 +0200250 const std::string& multicastPort,
Davide Pesavento292e5e12015-03-13 02:08:33 +0100251 const std::string& networkInterfaceName/* = ""*/)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100252{
Davide Pesavento292e5e12015-03-13 02:08:33 +0100253 udp::Endpoint localEndpoint(ip::address::from_string(localIp),
Chengyu Fan4381fb62015-01-14 11:37:04 -0700254 boost::lexical_cast<uint16_t>(multicastPort));
Davide Pesavento292e5e12015-03-13 02:08:33 +0100255 udp::Endpoint multicastEndpoint(ip::address::from_string(multicastIp),
Chengyu Fan4381fb62015-01-14 11:37:04 -0700256 boost::lexical_cast<uint16_t>(multicastPort));
Chengyu Fan4381fb62015-01-14 11:37:04 -0700257 return createMulticastFace(localEndpoint, multicastEndpoint, networkInterfaceName);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100258}
259
260void
261UdpFactory::createFace(const FaceUri& uri,
Yukai Tu7c90e6d2015-07-11 12:21:46 +0800262 ndn::nfd::FacePersistency persistency,
Giulio Grassi624f6c62014-02-18 19:42:14 +0100263 const FaceCreatedCallback& onCreated,
264 const FaceConnectFailedCallback& onConnectFailed)
265{
Yukai Tu375dcb02015-08-01 13:04:43 +0800266 if (persistency == ndn::nfd::FacePersistency::FACE_PERSISTENCY_ON_DEMAND) {
267 BOOST_THROW_EXCEPTION(Error("UdpFactory::createFace does not support creating on-demand face"));
Yukai Tu7c90e6d2015-07-11 12:21:46 +0800268 }
269
Chengyu Fan4381fb62015-01-14 11:37:04 -0700270 BOOST_ASSERT(uri.isCanonical());
Davide Pesavento292e5e12015-03-13 02:08:33 +0100271
Chengyu Fan4381fb62015-01-14 11:37:04 -0700272 boost::asio::ip::address ipAddress = boost::asio::ip::address::from_string(uri.getHost());
273 udp::Endpoint endpoint(ipAddress, boost::lexical_cast<uint16_t>(uri.getPort()));
Junxiao Shi79494162014-04-02 18:25:11 -0700274
Giulio Grassi624f6c62014-02-18 19:42:14 +0100275 if (endpoint.address().is_multicast()) {
276 onConnectFailed("The provided address is multicast. Please use createMulticastFace method");
277 return;
278 }
Junxiao Shi79494162014-04-02 18:25:11 -0700279
Davide Pesavento292e5e12015-03-13 02:08:33 +0100280 if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end()) {
281 onConnectFailed("Requested endpoint is prohibited "
282 "(reserved by this NFD or disallowed by face management protocol)");
283 return;
284 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600285
Giulio Grassi624f6c62014-02-18 19:42:14 +0100286 // very simple logic for now
Giulio Grassi624f6c62014-02-18 19:42:14 +0100287 for (ChannelMap::iterator channel = m_channels.begin();
288 channel != m_channels.end();
289 ++channel)
290 {
291 if ((channel->first.address().is_v4() && endpoint.address().is_v4()) ||
292 (channel->first.address().is_v6() && endpoint.address().is_v6()))
293 {
Yukai Tu375dcb02015-08-01 13:04:43 +0800294 channel->second->connect(endpoint, persistency, onCreated, onConnectFailed);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100295 return;
296 }
297 }
Davide Pesavento292e5e12015-03-13 02:08:33 +0100298
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700299 onConnectFailed("No channels available to connect to " +
300 boost::lexical_cast<std::string>(endpoint));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100301}
302
303shared_ptr<UdpChannel>
304UdpFactory::findChannel(const udp::Endpoint& localEndpoint)
305{
306 ChannelMap::iterator i = m_channels.find(localEndpoint);
307 if (i != m_channels.end())
308 return i->second;
309 else
310 return shared_ptr<UdpChannel>();
311}
312
Yukai Tu0a49d342015-09-13 12:54:22 +0800313shared_ptr<face::LpFaceWrapper>
Giulio Grassi624f6c62014-02-18 19:42:14 +0100314UdpFactory::findMulticastFace(const udp::Endpoint& localEndpoint)
315{
316 MulticastFaceMap::iterator i = m_multicastFaces.find(localEndpoint);
317 if (i != m_multicastFaces.end())
318 return i->second;
319 else
Yukai Tu0a49d342015-09-13 12:54:22 +0800320 return nullptr;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100321}
322
Davide Pesavento292e5e12015-03-13 02:08:33 +0100323std::list<shared_ptr<const Channel>>
Steve DiBenedettoef04f272014-06-04 14:28:31 -0600324UdpFactory::getChannels() const
325{
Davide Pesavento292e5e12015-03-13 02:08:33 +0100326 std::list<shared_ptr<const Channel>> channels;
Steve DiBenedettoef04f272014-06-04 14:28:31 -0600327 for (ChannelMap::const_iterator i = m_channels.begin(); i != m_channels.end(); ++i)
328 {
329 channels.push_back(i->second);
330 }
331
332 return channels;
333}
334
Giulio Grassi624f6c62014-02-18 19:42:14 +0100335} // namespace nfd