blob: 3a55b7caa7c8df0adacc62025f8188b182fd3d8c [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"
27#include "core/global-io.hpp"
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060028#include "core/network-interface.hpp"
Giulio Grassi624f6c62014-02-18 19:42:14 +010029
Davide Pesavento292e5e12015-03-13 02:08:33 +010030#ifdef __linux__
31#include <cerrno> // for errno
32#include <cstring> // for std::strerror()
33#include <sys/socket.h> // for setsockopt()
Giulio Grassi6d7176d2014-04-16 16:08:48 +020034#endif
35
Giulio Grassi624f6c62014-02-18 19:42:14 +010036namespace nfd {
37
38using namespace boost::asio;
Junxiao Shi79494162014-04-02 18:25:11 -070039
Giulio Grassi624f6c62014-02-18 19:42:14 +010040NFD_LOG_INIT("UdpFactory");
41
Alexander Afanasyev70aaf8a2014-12-13 00:44:22 -080042static const boost::asio::ip::address_v4 ALL_V4_ENDPOINT(
43 boost::asio::ip::address_v4::from_string("0.0.0.0"));
44
45static const boost::asio::ip::address_v6 ALL_V6_ENDPOINT(
46 boost::asio::ip::address_v6::from_string("::"));
47
Giulio Grassi624f6c62014-02-18 19:42:14 +010048UdpFactory::UdpFactory(const std::string& defaultPort/* = "6363"*/)
49 : m_defaultPort(defaultPort)
50{
51}
52
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060053void
54UdpFactory::prohibitEndpoint(const udp::Endpoint& endpoint)
55{
56 using namespace boost::asio::ip;
57
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060058 const address& address = endpoint.address();
59
60 if (address.is_v4() && address == ALL_V4_ENDPOINT)
61 {
62 prohibitAllIpv4Endpoints(endpoint.port());
63 }
64 else if (endpoint.address().is_v6() && address == ALL_V6_ENDPOINT)
65 {
66 prohibitAllIpv6Endpoints(endpoint.port());
67 }
68
Alexander Afanasyev70aaf8a2014-12-13 00:44:22 -080069 NFD_LOG_TRACE("prohibiting UDP " << endpoint);
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060070
71 m_prohibitedEndpoints.insert(endpoint);
72}
73
74void
75UdpFactory::prohibitAllIpv4Endpoints(const uint16_t port)
76{
77 using namespace boost::asio::ip;
78
Davide Pesaventob499a602014-11-18 22:36:56 +010079 for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
80 for (const address_v4& addr : nic.ipv4Addresses) {
Alexander Afanasyev70aaf8a2014-12-13 00:44:22 -080081 if (addr != ALL_V4_ENDPOINT) {
82 prohibitEndpoint(udp::Endpoint(addr, port));
83 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060084 }
85
Alexander Afanasyev70aaf8a2014-12-13 00:44:22 -080086 if (nic.isBroadcastCapable() && nic.broadcastAddress != ALL_V4_ENDPOINT)
Davide Pesaventob499a602014-11-18 22:36:56 +010087 {
88 NFD_LOG_TRACE("prohibiting broadcast address: " << nic.broadcastAddress.to_string());
89 prohibitEndpoint(udp::Endpoint(nic.broadcastAddress, port));
90 }
91 }
92
Steve DiBenedettoca53ac62014-03-27 19:58:40 -060093 prohibitEndpoint(udp::Endpoint(address::from_string("255.255.255.255"), port));
94}
95
96void
97UdpFactory::prohibitAllIpv6Endpoints(const uint16_t port)
98{
99 using namespace boost::asio::ip;
100
Davide Pesaventob499a602014-11-18 22:36:56 +0100101 for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
102 for (const address_v6& addr : nic.ipv6Addresses) {
Alexander Afanasyev70aaf8a2014-12-13 00:44:22 -0800103 if (addr != ALL_V6_ENDPOINT) {
104 prohibitEndpoint(udp::Endpoint(addr, port));
105 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600106 }
Davide Pesaventob499a602014-11-18 22:36:56 +0100107 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600108}
109
Giulio Grassi624f6c62014-02-18 19:42:14 +0100110shared_ptr<UdpChannel>
111UdpFactory::createChannel(const udp::Endpoint& endpoint,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700112 const time::seconds& timeout)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100113{
Davide Pesaventob499a602014-11-18 22:36:56 +0100114 NFD_LOG_DEBUG("Creating unicast channel " << endpoint);
Junxiao Shi79494162014-04-02 18:25:11 -0700115
Giulio Grassi624f6c62014-02-18 19:42:14 +0100116 shared_ptr<UdpChannel> channel = findChannel(endpoint);
117 if (static_cast<bool>(channel))
118 return channel;
119
Junxiao Shi79494162014-04-02 18:25:11 -0700120 //checking if the endpoint is already in use for multicast face
Giulio Grassi624f6c62014-02-18 19:42:14 +0100121 shared_ptr<MulticastUdpFace> multicast = findMulticastFace(endpoint);
122 if (static_cast<bool>(multicast))
123 throw Error("Cannot create the requested UDP unicast channel, local "
124 "endpoint is already allocated for a UDP multicast face");
Junxiao Shi79494162014-04-02 18:25:11 -0700125
Giulio Grassi624f6c62014-02-18 19:42:14 +0100126 if (endpoint.address().is_multicast()) {
127 throw Error("This method is only for unicast channel. The provided "
128 "endpoint is multicast. Use createMulticastFace to "
129 "create a multicast face");
130 }
131
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700132 channel = make_shared<UdpChannel>(endpoint, timeout);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100133 m_channels[endpoint] = channel;
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600134 prohibitEndpoint(endpoint);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100135
136 return channel;
137}
138
139shared_ptr<UdpChannel>
Alexander Afanasyev0e156df2015-01-26 22:33:43 -0800140UdpFactory::createChannel(const std::string& localIp,
Giulio Grassi624f6c62014-02-18 19:42:14 +0100141 const std::string& localPort,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700142 const time::seconds& timeout)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100143{
Alexander Afanasyev0e156df2015-01-26 22:33:43 -0800144 using namespace boost::asio::ip;
145 udp::Endpoint endpoint(address::from_string(localIp), boost::lexical_cast<uint16_t>(localPort));
146 return createChannel(endpoint, timeout);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100147}
148
149shared_ptr<MulticastUdpFace>
150UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint,
Giulio Grassi6d7176d2014-04-16 16:08:48 +0200151 const udp::Endpoint& multicastEndpoint,
Davide Pesavento292e5e12015-03-13 02:08:33 +0100152 const std::string& networkInterfaceName/* = ""*/)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100153{
Davide Pesavento292e5e12015-03-13 02:08:33 +0100154 // checking if the local and multicast endpoints are already in use for a multicast face
155 shared_ptr<MulticastUdpFace> face = findMulticastFace(localEndpoint);
156 if (static_cast<bool>(face)) {
157 if (face->getMulticastGroup() == multicastEndpoint)
158 return face;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100159 else
160 throw Error("Cannot create the requested UDP multicast face, local "
161 "endpoint is already allocated for a UDP multicast face "
162 "on a different multicast group");
163 }
Junxiao Shi79494162014-04-02 18:25:11 -0700164
Davide Pesavento292e5e12015-03-13 02:08:33 +0100165 // checking if the local endpoint is already in use for a unicast channel
Giulio Grassi624f6c62014-02-18 19:42:14 +0100166 shared_ptr<UdpChannel> unicast = findChannel(localEndpoint);
167 if (static_cast<bool>(unicast)) {
168 throw Error("Cannot create the requested UDP multicast face, local "
169 "endpoint is already allocated for a UDP unicast channel");
170 }
171
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600172 if (m_prohibitedEndpoints.find(multicastEndpoint) != m_prohibitedEndpoints.end()) {
173 throw Error("Cannot create the requested UDP multicast face, "
174 "remote endpoint is owned by this NFD instance");
175 }
176
Giulio Grassi624f6c62014-02-18 19:42:14 +0100177 if (localEndpoint.address().is_v6() || multicastEndpoint.address().is_v6()) {
178 throw Error("IPv6 multicast is not supported yet. Please provide an IPv4 address");
179 }
Junxiao Shi79494162014-04-02 18:25:11 -0700180
Giulio Grassi624f6c62014-02-18 19:42:14 +0100181 if (localEndpoint.port() != multicastEndpoint.port()) {
182 throw Error("Cannot create the requested UDP multicast face, "
183 "both endpoints should have the same port number. ");
184 }
185
186 if (!multicastEndpoint.address().is_multicast()) {
187 throw Error("Cannot create the requested UDP multicast face, "
188 "the multicast group given as input is not a multicast address");
189 }
190
Davide Pesavento292e5e12015-03-13 02:08:33 +0100191 ip::udp::socket receiveSocket(getGlobalIoService());
192 receiveSocket.open(multicastEndpoint.protocol());
193 receiveSocket.set_option(ip::udp::socket::reuse_address(true));
194 receiveSocket.bind(multicastEndpoint);
Junxiao Shi79494162014-04-02 18:25:11 -0700195
Davide Pesavento292e5e12015-03-13 02:08:33 +0100196 ip::udp::socket sendSocket(getGlobalIoService());
197 sendSocket.open(multicastEndpoint.protocol());
198 sendSocket.set_option(ip::udp::socket::reuse_address(true));
199 sendSocket.set_option(ip::multicast::enable_loopback(false));
200 sendSocket.bind(udp::Endpoint(ip::address_v4::any(), multicastEndpoint.port()));
201 if (localEndpoint.address() != ALL_V4_ENDPOINT)
202 sendSocket.set_option(ip::multicast::outbound_interface(localEndpoint.address().to_v4()));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100203
Davide Pesavento292e5e12015-03-13 02:08:33 +0100204 sendSocket.set_option(ip::multicast::join_group(multicastEndpoint.address().to_v4(),
205 localEndpoint.address().to_v4()));
206 receiveSocket.set_option(ip::multicast::join_group(multicastEndpoint.address().to_v4(),
Alexander Afanasyevdc017002014-06-18 16:37:54 -0700207 localEndpoint.address().to_v4()));
208
Davide Pesavento292e5e12015-03-13 02:08:33 +0100209#ifdef __linux__
210 /*
211 * On Linux, if there is more than one MulticastUdpFace for the same multicast
212 * group but they are bound to different network interfaces, the socket needs
213 * to be bound to the specific interface using SO_BINDTODEVICE, otherwise the
214 * face will receive all packets sent to the other interfaces as well.
215 * This happens only on Linux. On OS X, the ip::multicast::join_group option
216 * is enough to get the desired behaviour.
217 */
Giulio Grassi6d7176d2014-04-16 16:08:48 +0200218 if (!networkInterfaceName.empty()) {
Davide Pesavento292e5e12015-03-13 02:08:33 +0100219 if (::setsockopt(receiveSocket.native_handle(), SOL_SOCKET, SO_BINDTODEVICE,
220 networkInterfaceName.c_str(), networkInterfaceName.size() + 1) < 0) {
221 throw Error("Cannot bind multicast face to " + networkInterfaceName +
222 ": " + std::strerror(errno));
Giulio Grassi6d7176d2014-04-16 16:08:48 +0200223 }
224 }
Giulio Grassi6d7176d2014-04-16 16:08:48 +0200225#endif
226
Davide Pesavento292e5e12015-03-13 02:08:33 +0100227 face = make_shared<MulticastUdpFace>(multicastEndpoint, FaceUri(localEndpoint),
228 std::move(receiveSocket), std::move(sendSocket));
Junxiao Shic099ddb2014-12-25 20:53:20 -0700229
Davide Pesavento292e5e12015-03-13 02:08:33 +0100230 face->onFail.connectSingleShot([this, localEndpoint] (const std::string& reason) {
231 m_multicastFaces.erase(localEndpoint);
232 });
233 m_multicastFaces[localEndpoint] = face;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100234
Davide Pesavento292e5e12015-03-13 02:08:33 +0100235 return face;
Giulio Grassi624f6c62014-02-18 19:42:14 +0100236}
237
238shared_ptr<MulticastUdpFace>
239UdpFactory::createMulticastFace(const std::string& localIp,
240 const std::string& multicastIp,
Giulio Grassi6d7176d2014-04-16 16:08:48 +0200241 const std::string& multicastPort,
Davide Pesavento292e5e12015-03-13 02:08:33 +0100242 const std::string& networkInterfaceName/* = ""*/)
Giulio Grassi624f6c62014-02-18 19:42:14 +0100243{
Davide Pesavento292e5e12015-03-13 02:08:33 +0100244 udp::Endpoint localEndpoint(ip::address::from_string(localIp),
Chengyu Fan4381fb62015-01-14 11:37:04 -0700245 boost::lexical_cast<uint16_t>(multicastPort));
Davide Pesavento292e5e12015-03-13 02:08:33 +0100246 udp::Endpoint multicastEndpoint(ip::address::from_string(multicastIp),
Chengyu Fan4381fb62015-01-14 11:37:04 -0700247 boost::lexical_cast<uint16_t>(multicastPort));
Chengyu Fan4381fb62015-01-14 11:37:04 -0700248 return createMulticastFace(localEndpoint, multicastEndpoint, networkInterfaceName);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100249}
250
251void
252UdpFactory::createFace(const FaceUri& uri,
Yukai Tu7c90e6d2015-07-11 12:21:46 +0800253 ndn::nfd::FacePersistency persistency,
Giulio Grassi624f6c62014-02-18 19:42:14 +0100254 const FaceCreatedCallback& onCreated,
255 const FaceConnectFailedCallback& onConnectFailed)
256{
Yukai Tu7c90e6d2015-07-11 12:21:46 +0800257 if (persistency != ndn::nfd::FACE_PERSISTENCY_PERSISTENT) {
258 throw Error("UdpFactory only supports persistent face");
259 }
260
Chengyu Fan4381fb62015-01-14 11:37:04 -0700261 BOOST_ASSERT(uri.isCanonical());
Davide Pesavento292e5e12015-03-13 02:08:33 +0100262
Chengyu Fan4381fb62015-01-14 11:37:04 -0700263 boost::asio::ip::address ipAddress = boost::asio::ip::address::from_string(uri.getHost());
264 udp::Endpoint endpoint(ipAddress, boost::lexical_cast<uint16_t>(uri.getPort()));
Junxiao Shi79494162014-04-02 18:25:11 -0700265
Giulio Grassi624f6c62014-02-18 19:42:14 +0100266 if (endpoint.address().is_multicast()) {
267 onConnectFailed("The provided address is multicast. Please use createMulticastFace method");
268 return;
269 }
Junxiao Shi79494162014-04-02 18:25:11 -0700270
Davide Pesavento292e5e12015-03-13 02:08:33 +0100271 if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end()) {
272 onConnectFailed("Requested endpoint is prohibited "
273 "(reserved by this NFD or disallowed by face management protocol)");
274 return;
275 }
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600276
Giulio Grassi624f6c62014-02-18 19:42:14 +0100277 // very simple logic for now
Giulio Grassi624f6c62014-02-18 19:42:14 +0100278 for (ChannelMap::iterator channel = m_channels.begin();
279 channel != m_channels.end();
280 ++channel)
281 {
282 if ((channel->first.address().is_v4() && endpoint.address().is_v4()) ||
283 (channel->first.address().is_v6() && endpoint.address().is_v6()))
284 {
Steve DiBenedettoca53ac62014-03-27 19:58:40 -0600285 channel->second->connect(endpoint, onCreated, onConnectFailed);
Giulio Grassi624f6c62014-02-18 19:42:14 +0100286 return;
287 }
288 }
Davide Pesavento292e5e12015-03-13 02:08:33 +0100289
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700290 onConnectFailed("No channels available to connect to " +
291 boost::lexical_cast<std::string>(endpoint));
Giulio Grassi624f6c62014-02-18 19:42:14 +0100292}
293
294shared_ptr<UdpChannel>
295UdpFactory::findChannel(const udp::Endpoint& localEndpoint)
296{
297 ChannelMap::iterator i = m_channels.find(localEndpoint);
298 if (i != m_channels.end())
299 return i->second;
300 else
301 return shared_ptr<UdpChannel>();
302}
303
304shared_ptr<MulticastUdpFace>
305UdpFactory::findMulticastFace(const udp::Endpoint& localEndpoint)
306{
307 MulticastFaceMap::iterator i = m_multicastFaces.find(localEndpoint);
308 if (i != m_multicastFaces.end())
309 return i->second;
310 else
311 return shared_ptr<MulticastUdpFace>();
312}
313
Davide Pesavento292e5e12015-03-13 02:08:33 +0100314std::list<shared_ptr<const Channel>>
Steve DiBenedettoef04f272014-06-04 14:28:31 -0600315UdpFactory::getChannels() const
316{
Davide Pesavento292e5e12015-03-13 02:08:33 +0100317 std::list<shared_ptr<const Channel>> channels;
Steve DiBenedettoef04f272014-06-04 14:28:31 -0600318 for (ChannelMap::const_iterator i = m_channels.begin(); i != m_channels.end(); ++i)
319 {
320 channels.push_back(i->second);
321 }
322
323 return channels;
324}
325
Giulio Grassi624f6c62014-02-18 19:42:14 +0100326} // namespace nfd