udp-factory.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
26 #include "udp-factory.hpp"
27 #include "generic-link-service.hpp"
29 #include "core/global-io.hpp"
30 #include <boost/range/adaptors.hpp>
31 #include <boost/range/algorithm/copy.hpp>
32 
33 #ifdef __linux__
34 #include <cerrno> // for errno
35 #include <cstring> // for std::strerror()
36 #include <sys/socket.h> // for setsockopt()
37 #endif // __linux__
38 
39 namespace nfd {
40 namespace face {
41 
42 namespace ip = boost::asio::ip;
43 
44 NFD_LOG_INIT("UdpFactory");
46 
47 const std::string&
49 {
50  static std::string id("udp");
51  return id;
52 }
53 
54 
55 void
58 {
59  // udp
60  // {
61  // port 6363
62  // enable_v4 yes
63  // enable_v6 yes
64  // idle_timeout 600
65  // keep_alive_interval 25 ; acceptable but ignored
66  // mcast yes
67  // mcast_group 224.0.23.170
68  // mcast_port 56363
69  // whitelist
70  // {
71  // *
72  // }
73  // blacklist
74  // {
75  // }
76  // }
77 
78  uint16_t port = 6363;
79  bool enableV4 = false;
80  bool enableV6 = false;
81  uint32_t idleTimeout = 600;
82  MulticastConfig mcastConfig;
83 
84  if (configSection) {
85  // These default to 'yes' but only if face_system.udp section is present
86  enableV4 = enableV6 = mcastConfig.isEnabled = true;
87 
88  for (const auto& pair : *configSection) {
89  const std::string& key = pair.first;
90  const ConfigSection& value = pair.second;
91 
92  if (key == "port") {
93  port = ConfigFile::parseNumber<uint16_t>(pair, "face_system.udp");
94  }
95  else if (key == "enable_v4") {
96  enableV4 = ConfigFile::parseYesNo(pair, "face_system.udp");
97  }
98  else if (key == "enable_v6") {
99  enableV6 = ConfigFile::parseYesNo(pair, "face_system.udp");
100  }
101  else if (key == "idle_timeout") {
102  idleTimeout = ConfigFile::parseNumber<uint32_t>(pair, "face_system.udp");
103  }
104  else if (key == "keep_alive_interval") {
105  // ignored
106  }
107  else if (key == "mcast") {
108  mcastConfig.isEnabled = ConfigFile::parseYesNo(pair, "face_system.udp");
109  }
110  else if (key == "mcast_group") {
111  const std::string& valueStr = value.get_value<std::string>();
112  boost::system::error_code ec;
113  mcastConfig.group.address(boost::asio::ip::address_v4::from_string(valueStr, ec));
114  if (ec) {
115  BOOST_THROW_EXCEPTION(ConfigFile::Error("face_system.udp.mcast_group: '" +
116  valueStr + "' cannot be parsed as an IPv4 address"));
117  }
118  else if (!mcastConfig.group.address().is_multicast()) {
119  BOOST_THROW_EXCEPTION(ConfigFile::Error("face_system.udp.mcast_group: '" +
120  valueStr + "' is not a multicast address"));
121  }
122  }
123  else if (key == "mcast_port") {
124  mcastConfig.group.port(ConfigFile::parseNumber<uint16_t>(pair, "face_system.udp"));
125  }
126  else if (key == "whitelist") {
127  mcastConfig.netifPredicate.parseWhitelist(value);
128  }
129  else if (key == "blacklist") {
130  mcastConfig.netifPredicate.parseBlacklist(value);
131  }
132  else {
133  BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system.udp." + key));
134  }
135  }
136 
137  if (!enableV4 && !enableV6 && !mcastConfig.isEnabled) {
138  BOOST_THROW_EXCEPTION(ConfigFile::Error(
139  "IPv4 and IPv6 UDP channels and UDP multicast have been disabled. "
140  "Remove face_system.udp section to disable UDP channels or enable at least one of them."));
141  }
142  }
143 
144  if (!context.isDryRun) {
145  if (enableV4) {
146  udp::Endpoint endpoint(ip::udp::v4(), port);
147  shared_ptr<UdpChannel> v4Channel = this->createChannel(endpoint, time::seconds(idleTimeout));
148  if (!v4Channel->isListening()) {
149  v4Channel->listen(context.addFace, nullptr);
150  }
151  providedSchemes.insert("udp");
152  providedSchemes.insert("udp4");
153  }
154  else if (providedSchemes.count("udp4") > 0) {
155  NFD_LOG_WARN("Cannot close udp4 channel after its creation");
156  }
157 
158  if (enableV6) {
159  udp::Endpoint endpoint(ip::udp::v6(), port);
160  shared_ptr<UdpChannel> v6Channel = this->createChannel(endpoint, time::seconds(idleTimeout));
161  if (!v6Channel->isListening()) {
162  v6Channel->listen(context.addFace, nullptr);
163  }
164  providedSchemes.insert("udp");
165  providedSchemes.insert("udp6");
166  }
167  else if (providedSchemes.count("udp6") > 0) {
168  NFD_LOG_WARN("Cannot close udp6 channel after its creation");
169  }
170 
171  if (m_mcastConfig.isEnabled != mcastConfig.isEnabled) {
172  if (mcastConfig.isEnabled) {
173  NFD_LOG_INFO("enabling multicast on " << mcastConfig.group);
174  }
175  else {
176  NFD_LOG_INFO("disabling multicast");
177  }
178  }
179  else if (m_mcastConfig.group != mcastConfig.group) {
180  NFD_LOG_INFO("changing multicast group from " << m_mcastConfig.group <<
181  " to " << mcastConfig.group);
182  }
183  else if (m_mcastConfig.netifPredicate != mcastConfig.netifPredicate) {
184  NFD_LOG_INFO("changing whitelist/blacklist");
185  }
186  else {
187  // There's no configuration change, but we still need to re-apply configuration because
188  // netifs may have changed.
189  }
190 
191  m_mcastConfig = mcastConfig;
192  this->applyMulticastConfig(context);
193  }
194 }
195 
196 void
197 UdpFactory::createFace(const FaceUri& uri,
198  ndn::nfd::FacePersistency persistency,
199  bool wantLocalFieldsEnabled,
200  const FaceCreatedCallback& onCreated,
201  const FaceCreationFailedCallback& onFailure)
202 {
203  BOOST_ASSERT(uri.isCanonical());
204 
205  if (persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
206  NFD_LOG_TRACE("createFace does not support FACE_PERSISTENCY_ON_DEMAND");
207  onFailure(406, "Outgoing unicast UDP faces do not support on-demand persistency");
208  return;
209  }
210 
211  udp::Endpoint endpoint(ip::address::from_string(uri.getHost()),
212  boost::lexical_cast<uint16_t>(uri.getPort()));
213 
214  if (endpoint.address().is_multicast()) {
215  NFD_LOG_TRACE("createFace does not support multicast faces");
216  onFailure(406, "Cannot create multicast UDP faces");
217  return;
218  }
219 
220  if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end()) {
221  NFD_LOG_TRACE("Requested endpoint is prohibited "
222  "(reserved by this NFD or disallowed by face management protocol)");
223  onFailure(406, "Requested endpoint is prohibited");
224  return;
225  }
226 
227  if (wantLocalFieldsEnabled) {
228  // UDP faces are never local
229  NFD_LOG_TRACE("createFace cannot create non-local face with local fields enabled");
230  onFailure(406, "Local fields can only be enabled on faces with local scope");
231  return;
232  }
233 
234  // very simple logic for now
235  for (const auto& i : m_channels) {
236  if ((i.first.address().is_v4() && endpoint.address().is_v4()) ||
237  (i.first.address().is_v6() && endpoint.address().is_v6())) {
238  i.second->connect(endpoint, persistency, onCreated, onFailure);
239  return;
240  }
241  }
242 
243  NFD_LOG_TRACE("No channels available to connect to " + boost::lexical_cast<std::string>(endpoint));
244  onFailure(504, "No channels available to connect");
245 }
246 
247 void
248 UdpFactory::prohibitEndpoint(const udp::Endpoint& endpoint)
249 {
250  if (endpoint.address().is_v4() &&
251  endpoint.address() == ip::address_v4::any()) {
252  prohibitAllIpv4Endpoints(endpoint.port());
253  }
254  else if (endpoint.address().is_v6() &&
255  endpoint.address() == ip::address_v6::any()) {
256  prohibitAllIpv6Endpoints(endpoint.port());
257  }
258 
259  NFD_LOG_TRACE("prohibiting UDP " << endpoint);
260  m_prohibitedEndpoints.insert(endpoint);
261 }
262 
263 void
264 UdpFactory::prohibitAllIpv4Endpoints(uint16_t port)
265 {
266  for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
267  for (const auto& addr : nic.ipv4Addresses) {
268  if (addr != ip::address_v4::any()) {
269  prohibitEndpoint(udp::Endpoint(addr, port));
270  }
271  }
272 
273  if (nic.isBroadcastCapable() &&
274  nic.broadcastAddress != ip::address_v4::any()) {
275  prohibitEndpoint(udp::Endpoint(nic.broadcastAddress, port));
276  }
277  }
278 
279  prohibitEndpoint(udp::Endpoint(ip::address_v4::broadcast(), port));
280 }
281 
282 void
283 UdpFactory::prohibitAllIpv6Endpoints(uint16_t port)
284 {
285  for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
286  for (const auto& addr : nic.ipv6Addresses) {
287  if (addr != ip::address_v6::any()) {
288  prohibitEndpoint(udp::Endpoint(addr, port));
289  }
290  }
291  }
292 }
293 
294 shared_ptr<UdpChannel>
296  const time::seconds& timeout)
297 {
298  NFD_LOG_DEBUG("Creating unicast channel " << endpoint);
299 
300  auto channel = findChannel(endpoint);
301  if (channel)
302  return channel;
303 
304  if (endpoint.address().is_multicast()) {
305  BOOST_THROW_EXCEPTION(Error("createChannel is only for unicast channels. The provided endpoint "
306  "is multicast. Use createMulticastFace to create a multicast face"));
307  }
308 
309  // check if the endpoint is already used by a multicast face
310  auto face = findMulticastFace(endpoint);
311  if (face) {
312  BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP unicast channel, local "
313  "endpoint is already allocated for a UDP multicast face"));
314  }
315 
316  channel = std::make_shared<UdpChannel>(endpoint, timeout);
317  m_channels[endpoint] = channel;
318  prohibitEndpoint(endpoint);
319 
320  return channel;
321 }
322 
323 shared_ptr<UdpChannel>
324 UdpFactory::createChannel(const std::string& localIp, const std::string& localPort,
325  const time::seconds& timeout)
326 {
327  udp::Endpoint endpoint(ip::address::from_string(localIp),
328  boost::lexical_cast<uint16_t>(localPort));
329  return createChannel(endpoint, timeout);
330 }
331 
332 std::vector<shared_ptr<const Channel>>
334 {
335  return getChannelsFromMap(m_channels);
336 }
337 
338 shared_ptr<UdpChannel>
339 UdpFactory::findChannel(const udp::Endpoint& localEndpoint) const
340 {
341  auto i = m_channels.find(localEndpoint);
342  if (i != m_channels.end())
343  return i->second;
344  else
345  return nullptr;
346 }
347 
348 shared_ptr<Face>
350  const udp::Endpoint& multicastEndpoint,
351  const std::string& networkInterfaceName/* = ""*/)
352 {
353  // checking if the local and multicast endpoints are already in use for a multicast face
354  auto face = findMulticastFace(localEndpoint);
355  if (face) {
356  if (face->getRemoteUri() == FaceUri(multicastEndpoint))
357  return face;
358  else
359  BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, local "
360  "endpoint is already allocated for a UDP multicast face "
361  "on a different multicast group"));
362  }
363 
364  // checking if the local endpoint is already in use for a unicast channel
365  auto unicastCh = findChannel(localEndpoint);
366  if (unicastCh) {
367  BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, local "
368  "endpoint is already allocated for a UDP unicast channel"));
369  }
370 
371  if (m_prohibitedEndpoints.find(multicastEndpoint) != m_prohibitedEndpoints.end()) {
372  BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, "
373  "remote endpoint is owned by this NFD instance"));
374  }
375 
376  if (localEndpoint.address().is_v6() || multicastEndpoint.address().is_v6()) {
377  BOOST_THROW_EXCEPTION(Error("IPv6 multicast is not supported yet. Please provide an IPv4 "
378  "address"));
379  }
380 
381  if (localEndpoint.port() != multicastEndpoint.port()) {
382  BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, "
383  "both endpoints should have the same port number. "));
384  }
385 
386  if (!multicastEndpoint.address().is_multicast()) {
387  BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, "
388  "the multicast group given as input is not a multicast address"));
389  }
390 
391  ip::udp::socket receiveSocket(getGlobalIoService());
392  receiveSocket.open(multicastEndpoint.protocol());
393  receiveSocket.set_option(ip::udp::socket::reuse_address(true));
394  receiveSocket.bind(multicastEndpoint);
395 
396  ip::udp::socket sendSocket(getGlobalIoService());
397  sendSocket.open(multicastEndpoint.protocol());
398  sendSocket.set_option(ip::udp::socket::reuse_address(true));
399  sendSocket.set_option(ip::multicast::enable_loopback(false));
400  sendSocket.bind(udp::Endpoint(ip::address_v4::any(), multicastEndpoint.port()));
401  if (localEndpoint.address() != ip::address_v4::any())
402  sendSocket.set_option(ip::multicast::outbound_interface(localEndpoint.address().to_v4()));
403 
404  sendSocket.set_option(ip::multicast::join_group(multicastEndpoint.address().to_v4(),
405  localEndpoint.address().to_v4()));
406  receiveSocket.set_option(ip::multicast::join_group(multicastEndpoint.address().to_v4(),
407  localEndpoint.address().to_v4()));
408 
409 #ifdef __linux__
410  /*
411  * On Linux, if there is more than one MulticastUdpFace for the same multicast
412  * group but they are bound to different network interfaces, the socket needs
413  * to be bound to the specific interface using SO_BINDTODEVICE, otherwise the
414  * face will receive all packets sent to the other interfaces as well.
415  * This happens only on Linux. On OS X, the ip::multicast::join_group option
416  * is enough to get the desired behaviour.
417  */
418  if (!networkInterfaceName.empty()) {
419  if (::setsockopt(receiveSocket.native_handle(), SOL_SOCKET, SO_BINDTODEVICE,
420  networkInterfaceName.c_str(), networkInterfaceName.size() + 1) < 0) {
421  BOOST_THROW_EXCEPTION(Error("Cannot bind multicast face to " + networkInterfaceName +
422  ": " + std::strerror(errno)));
423  }
424  }
425 #endif // __linux__
426 
427  auto linkService = make_unique<GenericLinkService>();
428  auto transport = make_unique<MulticastUdpTransport>(localEndpoint, multicastEndpoint,
429  std::move(receiveSocket),
430  std::move(sendSocket));
431  face = make_shared<Face>(std::move(linkService), std::move(transport));
432 
433  m_mcastFaces[localEndpoint] = face;
434  connectFaceClosedSignal(*face, [this, localEndpoint] { m_mcastFaces.erase(localEndpoint); });
435 
436  return face;
437 }
438 
439 shared_ptr<Face>
440 UdpFactory::createMulticastFace(const std::string& localIp,
441  const std::string& multicastIp,
442  const std::string& multicastPort,
443  const std::string& networkInterfaceName/* = ""*/)
444 {
445  udp::Endpoint localEndpoint(ip::address::from_string(localIp),
446  boost::lexical_cast<uint16_t>(multicastPort));
447  udp::Endpoint multicastEndpoint(ip::address::from_string(multicastIp),
448  boost::lexical_cast<uint16_t>(multicastPort));
449  return createMulticastFace(localEndpoint, multicastEndpoint, networkInterfaceName);
450 }
451 
452 shared_ptr<Face>
453 UdpFactory::findMulticastFace(const udp::Endpoint& localEndpoint) const
454 {
455  auto i = m_mcastFaces.find(localEndpoint);
456  if (i != m_mcastFaces.end())
457  return i->second;
458  else
459  return nullptr;
460 }
461 
462 void
463 UdpFactory::applyMulticastConfig(const FaceSystem::ConfigContext& context)
464 {
465  // collect old faces
466  std::set<shared_ptr<Face>> oldFaces;
467  boost::copy(m_mcastFaces | boost::adaptors::map_values,
468  std::inserter(oldFaces, oldFaces.end()));
469 
470  if (m_mcastConfig.isEnabled) {
471  // determine interfaces on which faces should be created or retained
472  auto capableNetifRange = context.listNetifs() |
473  boost::adaptors::filtered([this] (const NetworkInterfaceInfo& netif) {
474  return netif.isUp() && netif.isMulticastCapable() &&
475  !netif.ipv4Addresses.empty() &&
476  m_mcastConfig.netifPredicate(netif);
477  });
478 
479  bool needIfname = false;
480 #ifdef __linux__
481  std::vector<NetworkInterfaceInfo> capableNetifs;
482  boost::copy(capableNetifRange, std::back_inserter(capableNetifs));
483  // on Linux, ifname is needed to create more than one UDP multicast face on the same group
484  needIfname = capableNetifs.size() > 1;
485 #else
486  auto& capableNetifs = capableNetifRange;
487 #endif // __linux__
488 
489  // create faces
490  for (const auto& netif : capableNetifs) {
491  udp::Endpoint localEndpoint(netif.ipv4Addresses.front(), m_mcastConfig.group.port());
492  shared_ptr<Face> face = this->createMulticastFace(localEndpoint, m_mcastConfig.group,
493  needIfname ? netif.name : "");
494  if (face->getId() == INVALID_FACEID) {
495  // new face: register with forwarding
496  context.addFace(face);
497  }
498  else {
499  // existing face: don't destroy
500  oldFaces.erase(face);
501  }
502  }
503  }
504 
505  // destroy old faces that are not needed in new configuration
506  for (const auto& face : oldFaces) {
507  face->close();
508  }
509 }
510 
511 } // namespace face
512 } // namespace nfd
shared_ptr< UdpChannel > createChannel(const udp::Endpoint &localEndpoint, const time::seconds &timeout=time::seconds(600))
Create UDP-based channel using udp::Endpoint.
#define NFD_LOG_DEBUG(expression)
Definition: logger.hpp:161
contains information about a network interface
std::set< std::string > providedSchemes
FaceUri schemes provided by this ProtocolFactory.
void connectFaceClosedSignal(Face &face, const std::function< void()> &f)
invokes a callback when the face is closed
Definition: channel.cpp:41
void createFace(const FaceUri &uri, ndn::nfd::FacePersistency persistency, bool wantLocalFieldsEnabled, const FaceCreatedCallback &onCreated, const FaceCreationFailedCallback &onFailure) override
Try to create Face using the supplied FaceUri.
static bool parseYesNo(const ConfigSection &node, const std::string &key, const std::string &sectionName)
parse a config option that can be either "yes" or "no"
Definition: config-file.cpp:62
Exception of UdpFactory.
Definition: udp-factory.hpp:46
#define NFD_REGISTER_PROTOCOL_FACTORY(PF)
registers a protocol factory
std::vector< NetworkInterfaceInfo > listNetworkInterfaces()
List configured network interfaces on the system and their info.
#define NFD_LOG_WARN(expression)
Definition: logger.hpp:163
std::vector< boost::asio::ip::address_v4 > ipv4Addresses
context for processing a config section in ProtocolFactory
Definition: face-system.hpp:77
static std::vector< shared_ptr< const Channel > > getChannelsFromMap(const ChannelMap &channelMap)
#define NFD_LOG_INFO(expression)
Definition: logger.hpp:162
Copyright (c) 2014-2015, Regents of the University of California, Arizona Board of Regents...
boost::optional< const ConfigSection & > OptionalConfigSection
an optional config file section
Definition: config-file.hpp:41
boost::property_tree::ptree ConfigSection
a config file section
Definition: config-file.hpp:37
boost::asio::ip::udp::endpoint Endpoint
function< void(uint32_t status, const std::string &reason)> FaceCreationFailedCallback
Prototype for the callback that is invoked when the face fails to be created.
Definition: channel.hpp:44
void processConfig(OptionalConfigSection configSection, FaceSystem::ConfigContext &context) override
process face_system.udp config section
Definition: udp-factory.cpp:56
static const std::string & getId()
Definition: udp-factory.cpp:48
#define NFD_LOG_INIT(name)
Definition: logger.hpp:122
#define NFD_LOG_TRACE(expression)
Definition: logger.hpp:160
function< void(const shared_ptr< Face > &newFace)> FaceCreatedCallback
Prototype for the callback that is invoked when the face is created (as a response to incoming connec...
Definition: channel.hpp:38
std::vector< shared_ptr< const Channel > > getChannels() const override
const FaceId INVALID_FACEID
indicates an invalid FaceId
Definition: face.hpp:42
shared_ptr< Face > createMulticastFace(const udp::Endpoint &localEndpoint, const udp::Endpoint &multicastEndpoint, const std::string &networkInterfaceName="")
Create MulticastUdpFace using udp::Endpoint.
boost::asio::io_service & getGlobalIoService()
Definition: global-io.cpp:41