NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.0: NDN, CCN, CCNx, content centric networks
API Documentation
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"
31 
32 #ifdef __linux__
33 #include <cerrno> // for errno
34 #include <cstring> // for std::strerror()
35 #include <sys/socket.h> // for setsockopt()
36 #endif
37 
38 namespace nfd {
39 
40 namespace ip = boost::asio::ip;
41 
42 NFD_LOG_INIT("UdpFactory");
43 
44 void
45 UdpFactory::prohibitEndpoint(const udp::Endpoint& endpoint)
46 {
47  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  }
55 
56  NFD_LOG_TRACE("prohibiting UDP " << endpoint);
57  m_prohibitedEndpoints.insert(endpoint);
58 }
59 
60 void
61 UdpFactory::prohibitAllIpv4Endpoints(uint16_t port)
62 {
63  for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
64  for (const auto& addr : nic.ipv4Addresses) {
65  if (addr != ip::address_v4::any()) {
66  prohibitEndpoint(udp::Endpoint(addr, port));
67  }
68  }
69 
70  if (nic.isBroadcastCapable() &&
71  nic.broadcastAddress != ip::address_v4::any()) {
72  prohibitEndpoint(udp::Endpoint(nic.broadcastAddress, port));
73  }
74  }
75 
76  prohibitEndpoint(udp::Endpoint(ip::address_v4::broadcast(), port));
77 }
78 
79 void
80 UdpFactory::prohibitAllIpv6Endpoints(uint16_t port)
81 {
82  for (const NetworkInterfaceInfo& nic : listNetworkInterfaces()) {
83  for (const auto& addr : nic.ipv6Addresses) {
84  if (addr != ip::address_v6::any()) {
85  prohibitEndpoint(udp::Endpoint(addr, port));
86  }
87  }
88  }
89 }
90 
91 shared_ptr<UdpChannel>
93  const time::seconds& timeout)
94 {
95  NFD_LOG_DEBUG("Creating unicast channel " << endpoint);
96 
97  auto channel = findChannel(endpoint);
98  if (channel)
99  return channel;
100 
101  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 
106  // check if the endpoint is already used by a multicast face
107  auto face = findMulticastFace(endpoint);
108  if (face) {
109  BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP unicast channel, local "
110  "endpoint is already allocated for a UDP multicast face"));
111  }
112 
113  channel = make_shared<UdpChannel>(endpoint, timeout);
114  m_channels[endpoint] = channel;
115  prohibitEndpoint(endpoint);
116 
117  return channel;
118 }
119 
120 shared_ptr<UdpChannel>
121 UdpFactory::createChannel(const std::string& localIp, const std::string& localPort,
122  const time::seconds& timeout)
123 {
124  udp::Endpoint endpoint(ip::address::from_string(localIp),
125  boost::lexical_cast<uint16_t>(localPort));
126  return createChannel(endpoint, timeout);
127 }
128 
129 shared_ptr<Face>
131  const udp::Endpoint& multicastEndpoint,
132  const std::string& networkInterfaceName/* = ""*/)
133 {
134  // checking if the local and multicast endpoints are already in use for a multicast face
135  auto face = findMulticastFace(localEndpoint);
136  if (face) {
137  if (face->getRemoteUri() == FaceUri(multicastEndpoint))
138  return face;
139  else
140  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"));
143  }
144 
145  // checking if the local endpoint is already in use for a unicast channel
146  auto unicastCh = findChannel(localEndpoint);
147  if (unicastCh) {
148  BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, local "
149  "endpoint is already allocated for a UDP unicast channel"));
150  }
151 
152  if (m_prohibitedEndpoints.find(multicastEndpoint) != m_prohibitedEndpoints.end()) {
153  BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, "
154  "remote endpoint is owned by this NFD instance"));
155  }
156 
157  if (localEndpoint.address().is_v6() || multicastEndpoint.address().is_v6()) {
158  BOOST_THROW_EXCEPTION(Error("IPv6 multicast is not supported yet. Please provide an IPv4 "
159  "address"));
160  }
161 
162  if (localEndpoint.port() != multicastEndpoint.port()) {
163  BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, "
164  "both endpoints should have the same port number. "));
165  }
166 
167  if (!multicastEndpoint.address().is_multicast()) {
168  BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, "
169  "the multicast group given as input is not a multicast address"));
170  }
171 
172  ip::udp::socket receiveSocket(getGlobalIoService());
173  receiveSocket.open(multicastEndpoint.protocol());
174  receiveSocket.set_option(ip::udp::socket::reuse_address(true));
175  receiveSocket.bind(multicastEndpoint);
176 
177  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()));
182  if (localEndpoint.address() != ip::address_v4::any())
183  sendSocket.set_option(ip::multicast::outbound_interface(localEndpoint.address().to_v4()));
184 
185  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(),
188  localEndpoint.address().to_v4()));
189 
190 #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  */
199  if (!networkInterfaceName.empty()) {
200  if (::setsockopt(receiveSocket.native_handle(), SOL_SOCKET, SO_BINDTODEVICE,
201  networkInterfaceName.c_str(), networkInterfaceName.size() + 1) < 0) {
202  BOOST_THROW_EXCEPTION(Error("Cannot bind multicast face to " + networkInterfaceName +
203  ": " + std::strerror(errno)));
204  }
205  }
206 #endif
207 
208  auto linkService = make_unique<face::GenericLinkService>();
209  auto transport = make_unique<face::MulticastUdpTransport>(localEndpoint, multicastEndpoint,
210  std::move(receiveSocket),
211  std::move(sendSocket));
212  face = make_shared<Face>(std::move(linkService), std::move(transport));
213 
214  m_multicastFaces[localEndpoint] = face;
215  connectFaceClosedSignal(*face, [this, localEndpoint] { m_multicastFaces.erase(localEndpoint); });
216 
217  return face;
218 }
219 
220 shared_ptr<Face>
221 UdpFactory::createMulticastFace(const std::string& localIp,
222  const std::string& multicastIp,
223  const std::string& multicastPort,
224  const std::string& networkInterfaceName/* = ""*/)
225 {
226  udp::Endpoint localEndpoint(ip::address::from_string(localIp),
227  boost::lexical_cast<uint16_t>(multicastPort));
228  udp::Endpoint multicastEndpoint(ip::address::from_string(multicastIp),
229  boost::lexical_cast<uint16_t>(multicastPort));
230  return createMulticastFace(localEndpoint, multicastEndpoint, networkInterfaceName);
231 }
232 
233 void
235  ndn::nfd::FacePersistency persistency,
236  const FaceCreatedCallback& onCreated,
237  const FaceCreationFailedCallback& onConnectFailed)
238 {
239  BOOST_ASSERT(uri.isCanonical());
240 
241  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()));
247 
248  if (endpoint.address().is_multicast()) {
249  onConnectFailed("The provided address is multicast. Please use createMulticastFace method");
250  return;
251  }
252 
253  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  }
258 
259  // very simple logic for now
260  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);
264  return;
265  }
266  }
267 
268  onConnectFailed("No channels available to connect to " + boost::lexical_cast<std::string>(endpoint));
269 }
270 
271 std::vector<shared_ptr<const Channel>>
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;
281 }
282 
283 shared_ptr<UdpChannel>
284 UdpFactory::findChannel(const udp::Endpoint& localEndpoint) const
285 {
286  auto i = m_channels.find(localEndpoint);
287  if (i != m_channels.end())
288  return i->second;
289  else
290  return nullptr;
291 }
292 
293 shared_ptr<Face>
294 UdpFactory::findMulticastFace(const udp::Endpoint& localEndpoint) const
295 {
296  auto i = m_multicastFaces.find(localEndpoint);
297  if (i != m_multicastFaces.end())
298  return i->second;
299  else
300  return nullptr;
301 }
302 
303 } // namespace nfd
#define NFD_LOG_DEBUG(expression)
Definition: logger.hpp:55
function< void(const std::string &reason)> FaceCreationFailedCallback
Prototype for the callback that is invoked when the face fails to be created.
Definition: channel.hpp:44
represents the underlying protocol and address used by a Face
Definition: face-uri.hpp:44
shared_ptr< Face > createMulticastFace(const udp::Endpoint &localEndpoint, const udp::Endpoint &multicastEndpoint, const std::string &networkInterfaceName="")
Create MulticastUdpFace using udp::Endpoint.
bool isCanonical() const
determine whether this FaceUri is in canonical form
Definition: face-uri.cpp:507
void connectFaceClosedSignal(Face &face, const std::function< void()> &f)
invokes a callback when the face is closed
Definition: channel.cpp:41
contains information about a network interface
detail::SimulatorIo & getGlobalIoService()
Definition: global-io.cpp:48
shared_ptr< UdpChannel > createChannel(const udp::Endpoint &localEndpoint, const time::seconds &timeout=time::seconds(600))
Create UDP-based channel using udp::Endpoint.
Definition: udp-factory.cpp:92
Exception of UdpFactory.
Definition: udp-factory.hpp:42
virtual std::vector< shared_ptr< const Channel > > getChannels() const 1
std::vector< NetworkInterfaceInfo > listNetworkInterfaces()
List configured network interfaces on the system and their info.
Copyright (c) 2011-2015 Regents of the University of California.
Definition: ndn-common.hpp:40
const std::string & getPort() const
get port
Definition: face-uri.hpp:123
virtual void createFace(const FaceUri &uri, ndn::nfd::FacePersistency persistency, const FaceCreatedCallback &onCreated, const FaceCreationFailedCallback &onConnectFailed) 1
Try to create Face using the supplied FaceUri.
boost::asio::ip::udp::endpoint Endpoint
Definition: udp-channel.hpp:34
#define NFD_LOG_INIT(name)
Definition: logger.hpp:34
#define NFD_LOG_TRACE(expression)
Definition: logger.hpp:54
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
const std::string & getHost() const
get host (domain)
Definition: face-uri.hpp:116