NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.5: NDN, CCN, CCNx, content centric networks
API Documentation
ethernet-factory.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2014-2018, 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.
10  *
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/>.
24  */
25 
26 #include "ethernet-factory.hpp"
27 #include "generic-link-service.hpp"
29 
30 #include <boost/range/adaptors.hpp>
31 #include <boost/range/algorithm/copy.hpp>
32 
33 namespace nfd {
34 namespace face {
35 
38 
39 const std::string&
41 {
42  static std::string id("ether");
43  return id;
44 }
45 
47  : ProtocolFactory(params)
48 {
49  m_netifAddConn = netmon->onInterfaceAdded.connect([this] (const auto& netif) {
50  this->applyUnicastConfigToNetif(netif);
51  this->applyMcastConfigToNetif(*netif);
52  });
53 }
54 
55 void
56 EthernetFactory::doProcessConfig(OptionalConfigSection configSection,
58 {
59  // ether
60  // {
61  // listen yes
62  // idle_timeout 600
63  // mcast yes
64  // mcast_group 01:00:5E:00:17:AA
65  // mcast_ad_hoc no
66  // whitelist
67  // {
68  // *
69  // }
70  // blacklist
71  // {
72  // }
73  // }
74 
75  UnicastConfig unicastConfig;
76  MulticastConfig mcastConfig;
77 
78  if (configSection) {
79  // listen and mcast default to 'yes' but only if face_system.ether section is present
80  unicastConfig.isEnabled = unicastConfig.wantListen = mcastConfig.isEnabled = true;
81 
82  for (const auto& pair : *configSection) {
83  const std::string& key = pair.first;
84  const ConfigSection& value = pair.second;
85 
86  if (key == "listen") {
87  unicastConfig.wantListen = ConfigFile::parseYesNo(pair, "face_system.ether");
88  }
89  else if (key == "idle_timeout") {
90  unicastConfig.idleTimeout = time::seconds(ConfigFile::parseNumber<uint32_t>(pair, "face_system.ether"));
91  }
92  else if (key == "mcast") {
93  mcastConfig.isEnabled = ConfigFile::parseYesNo(pair, "face_system.ether");
94  }
95  else if (key == "mcast_group") {
96  const std::string& valueStr = value.get_value<std::string>();
97  mcastConfig.group = ethernet::Address::fromString(valueStr);
98  if (mcastConfig.group.isNull()) {
99  BOOST_THROW_EXCEPTION(ConfigFile::Error("face_system.ether.mcast_group: '" +
100  valueStr + "' cannot be parsed as an Ethernet address"));
101  }
102  else if (!mcastConfig.group.isMulticast()) {
103  BOOST_THROW_EXCEPTION(ConfigFile::Error("face_system.ether.mcast_group: '" +
104  valueStr + "' is not a multicast address"));
105  }
106  }
107  else if (key == "mcast_ad_hoc") {
108  bool wantAdHoc = ConfigFile::parseYesNo(pair, "face_system.ether");
109  mcastConfig.linkType = wantAdHoc ? ndn::nfd::LINK_TYPE_AD_HOC : ndn::nfd::LINK_TYPE_MULTI_ACCESS;
110  }
111  else if (key == "whitelist") {
112  mcastConfig.netifPredicate.parseWhitelist(value);
113  }
114  else if (key == "blacklist") {
115  mcastConfig.netifPredicate.parseBlacklist(value);
116  }
117  else {
118  BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system.ether." + key));
119  }
120  }
121  }
122 
123  if (context.isDryRun) {
124  return;
125  }
126 
127  if (unicastConfig.isEnabled) {
128  if (m_unicastConfig.wantListen && !unicastConfig.wantListen && !m_channels.empty()) {
129  NFD_LOG_WARN("Cannot stop listening on Ethernet channels");
130  }
131  if (m_unicastConfig.idleTimeout != unicastConfig.idleTimeout && !m_channels.empty()) {
132  NFD_LOG_WARN("Idle timeout setting applies to new Ethernet channels only");
133  }
134  }
135  else if (m_unicastConfig.isEnabled && !m_channels.empty()) {
136  NFD_LOG_WARN("Cannot disable Ethernet channels after initialization");
137  }
138 
139  if (m_mcastConfig.isEnabled != mcastConfig.isEnabled) {
140  if (mcastConfig.isEnabled) {
141  NFD_LOG_INFO("enabling multicast on " << mcastConfig.group);
142  }
143  else {
144  NFD_LOG_INFO("disabling multicast");
145  }
146  }
147  else if (mcastConfig.isEnabled) {
148  if (m_mcastConfig.linkType != mcastConfig.linkType && !m_mcastFaces.empty()) {
149  NFD_LOG_WARN("Cannot change ad hoc setting on existing faces");
150  }
151  if (m_mcastConfig.group != mcastConfig.group) {
152  NFD_LOG_INFO("changing multicast group from " << m_mcastConfig.group <<
153  " to " << mcastConfig.group);
154  }
155  if (m_mcastConfig.netifPredicate != mcastConfig.netifPredicate) {
156  NFD_LOG_INFO("changing whitelist/blacklist");
157  }
158  }
159 
160  // Even if there's no configuration change, we still need to re-apply configuration because
161  // netifs may have changed.
162  m_unicastConfig = unicastConfig;
163  m_mcastConfig = mcastConfig;
164  this->applyConfig(context);
165 }
166 
167 void
168 EthernetFactory::doCreateFace(const CreateFaceRequest& req,
169  const FaceCreatedCallback& onCreated,
170  const FaceCreationFailedCallback& onFailure)
171 {
172  if (!req.localUri || req.localUri->getScheme() != "dev") {
173  NFD_LOG_TRACE("Cannot create unicast Ethernet face without dev:// LocalUri");
174  onFailure(406, "Creation of unicast Ethernet faces requires a LocalUri with dev:// scheme");
175  return;
176  }
177 
178  if (req.params.persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
179  NFD_LOG_TRACE("createFace does not support FACE_PERSISTENCY_ON_DEMAND");
180  onFailure(406, "Outgoing Ethernet faces do not support on-demand persistency");
181  return;
182  }
183 
184  ethernet::Address remoteEndpoint(ethernet::Address::fromString(req.remoteUri.getHost()));
185  std::string localEndpoint(req.localUri->getHost());
186 
187  if (remoteEndpoint.isMulticast()) {
188  NFD_LOG_TRACE("createFace does not support multicast faces");
189  onFailure(406, "Cannot create multicast Ethernet faces");
190  return;
191  }
192 
193  if (req.params.wantLocalFields) {
194  // Ethernet faces are never local
195  NFD_LOG_TRACE("createFace cannot create non-local face with local fields enabled");
196  onFailure(406, "Local fields can only be enabled on faces with local scope");
197  return;
198  }
199 
200  if (req.params.mtu && *req.params.mtu < Transport::MIN_MTU) {
201  // The specified MTU must be greater than the minimum possible
202  NFD_LOG_TRACE("createFace cannot create a face with an MTU less than " << Transport::MIN_MTU);
203  onFailure(406, "MTU cannot be less than " + to_string(Transport::MIN_MTU));
204  return;
205  }
206 
207  for (const auto& i : m_channels) {
208  if (i.first == localEndpoint) {
209  i.second->connect(remoteEndpoint, req.params, onCreated, onFailure);
210  return;
211  }
212  }
213 
214  NFD_LOG_TRACE("No channels available to connect to " << remoteEndpoint);
215  onFailure(504, "No channels available to connect");
216 }
217 
218 shared_ptr<EthernetChannel>
219 EthernetFactory::createChannel(const shared_ptr<const ndn::net::NetworkInterface>& localEndpoint,
220  time::nanoseconds idleTimeout)
221 {
222  auto it = m_channels.find(localEndpoint->getName());
223  if (it != m_channels.end())
224  return it->second;
225 
226  auto channel = std::make_shared<EthernetChannel>(localEndpoint, idleTimeout);
227  m_channels[localEndpoint->getName()] = channel;
228  return channel;
229 }
230 
231 std::vector<shared_ptr<const Channel>>
232 EthernetFactory::doGetChannels() const
233 {
234  return getChannelsFromMap(m_channels);
235 }
236 
237 shared_ptr<Face>
239  const ethernet::Address& address)
240 {
241  BOOST_ASSERT(address.isMulticast());
242 
243  auto key = std::make_pair(netif.getName(), address);
244  auto found = m_mcastFaces.find(key);
245  if (found != m_mcastFaces.end()) {
246  return found->second;
247  }
248 
250  opts.allowFragmentation = true;
251  opts.allowReassembly = true;
252 
253  auto linkService = make_unique<GenericLinkService>(opts);
254  auto transport = make_unique<MulticastEthernetTransport>(netif, address, m_mcastConfig.linkType);
255  auto face = make_shared<Face>(std::move(linkService), std::move(transport));
256 
257  m_mcastFaces[key] = face;
258  connectFaceClosedSignal(*face, [this, key] { m_mcastFaces.erase(key); });
259 
260  return face;
261 }
262 
263 shared_ptr<EthernetChannel>
264 EthernetFactory::applyUnicastConfigToNetif(const shared_ptr<const ndn::net::NetworkInterface>& netif)
265 {
266  if (!m_unicastConfig.isEnabled) {
267  return nullptr;
268  }
269 
270  if (netif->getType() != ndn::net::InterfaceType::ETHERNET) {
271  NFD_LOG_DEBUG("Not creating channel on " << netif->getName() << ": incompatible netif type");
272  return nullptr;
273  }
274 
275  if (!netif->isUp()) {
276  NFD_LOG_DEBUG("Not creating channel on " << netif->getName() << ": netif is down");
277  return nullptr;
278  }
279 
280  if (netif->getEthernetAddress().isNull()) {
281  NFD_LOG_DEBUG("Not creating channel on " << netif->getName() << ": invalid Ethernet address");
282  return nullptr;
283  }
284 
285  auto channel = this->createChannel(netif, m_unicastConfig.idleTimeout);
286  if (m_unicastConfig.wantListen && !channel->isListening()) {
287  try {
288  channel->listen(this->addFace, nullptr);
289  }
290  catch (const EthernetChannel::Error& e) {
291  NFD_LOG_WARN("Cannot listen on " << netif->getName() << ": " << e.what());
292  }
293  }
294  return channel;
295 }
296 
297 shared_ptr<Face>
298 EthernetFactory::applyMcastConfigToNetif(const ndn::net::NetworkInterface& netif)
299 {
300  if (!m_mcastConfig.isEnabled) {
301  return nullptr;
302  }
303 
305  NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": incompatible netif type");
306  return nullptr;
307  }
308 
309  if (!netif.isUp()) {
310  NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": netif is down");
311  return nullptr;
312  }
313 
314  if (!netif.canMulticast()) {
315  NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": netif cannot multicast");
316  return nullptr;
317  }
318 
319  if (netif.getEthernetAddress().isNull()) {
320  NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": invalid Ethernet address");
321  return nullptr;
322  }
323 
324  if (!m_mcastConfig.netifPredicate(netif)) {
325  NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": rejected by whitelist/blacklist");
326  return nullptr;
327  }
328 
329  NFD_LOG_DEBUG("Creating multicast face on " << netif.getName());
330  shared_ptr<Face> face;
331  try {
332  face = this->createMulticastFace(netif, m_mcastConfig.group);
333  }
334  catch (const EthernetTransport::Error& e) {
335  NFD_LOG_WARN("Cannot create multicast face on " << netif.getName() << ": " << e.what());
336  return nullptr;
337  }
338 
339  if (face->getId() == face::INVALID_FACEID) {
340  // new face: register with forwarding
341  this->addFace(face);
342  }
343  return face;
344 }
345 
346 void
347 EthernetFactory::applyConfig(const FaceSystem::ConfigContext&)
348 {
349  if (m_unicastConfig.isEnabled) {
350  providedSchemes.insert("ether");
351  }
352  else {
353  providedSchemes.erase("ether");
354  }
355 
356  // collect old multicast faces
357  std::set<shared_ptr<Face>> oldFaces;
358  boost::copy(m_mcastFaces | boost::adaptors::map_values, std::inserter(oldFaces, oldFaces.end()));
359 
360  // create channels and multicast faces if requested by config
361  for (const auto& netif : netmon->listNetworkInterfaces()) {
362  this->applyUnicastConfigToNetif(netif);
363 
364  auto face = this->applyMcastConfigToNetif(*netif);
365  if (face != nullptr) {
366  // don't destroy face
367  oldFaces.erase(face);
368  }
369  }
370 
371  // destroy old multicast faces that are not needed in new configuration
372  for (const auto& face : oldFaces) {
373  face->close();
374  }
375 }
376 
377 } // namespace face
378 } // namespace nfd
std::set< std::string > providedSchemes
FaceUri schemes provided by this protocol factory.
static Address fromString(const std::string &str)
Creates an Address from a string containing an Ethernet address in hexadecimal notation,...
Definition: ethernet.cpp:92
InterfaceType getType() const
Returns the hardware type of the interface.
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:59
shared_ptr< ndn::net::NetworkMonitor > netmon
NetworkMonitor for listing available network interfaces and monitoring their changes.
std::string getName() const
Returns the name of the interface, unique on the system.
#define NFD_LOG_WARN
Definition: logger.hpp:40
bool isNull() const
True if this is a null address (00:00:00:00:00:00)
Definition: ethernet.cpp:72
static constexpr ssize_t MIN_MTU
minimum MTU that may be set on a transport
Definition: transport.hpp:374
#define NFD_LOG_INFO
Definition: logger.hpp:39
NFD_REGISTER_PROTOCOL_FACTORY(EthernetFactory)
std::function< void(uint32_t status, const std::string &reason)> FaceCreationFailedCallback
Prototype for the callback that is invoked when a face fails to be created.
Definition: channel.hpp:44
boost::optional< const ConfigSection & > OptionalConfigSection
an optional config file section
Definition: config-file.hpp:41
std::function< void(const shared_ptr< Face > &face)> FaceCreatedCallback
Prototype for the callback that is invoked when a face is created (in response to an incoming connect...
Definition: channel.hpp:40
void connectFaceClosedSignal(Face &face, const std::function< void()> &f)
invokes a callback when the face is closed
Definition: channel.cpp:40
FaceCreatedCallback addFace
callback when a new face is created
Represents one network interface attached to the host.
Provides support for an underlying protocol.
Copyright (c) 2011-2015 Regents of the University of California.
Definition: ndn-common.hpp:40
#define NFD_LOG_TRACE
Definition: logger.hpp:37
EthernetFactory(const CtorParams &params)
bool canMulticast() const
Returns true if the interface supports multicast communication.
bool isUp() const
Returns true if the interface is administratively up.
Protocol factory for Ethernet.
Parameters to ProtocolFactory constructor.
boost::property_tree::ptree ConfigSection
a config file section
shared_ptr< Face > createMulticastFace(const ndn::net::NetworkInterface &localEndpoint, const ethernet::Address &group)
Create a face to communicate on the given Ethernet multicast group.
bool isMulticast() const
True if this is a multicast address.
Definition: ethernet.cpp:66
context for processing a config section in ProtocolFactory
Definition: face-system.hpp:96
represents an Ethernet hardware address
Definition: ethernet.hpp:52
Options that control the behavior of GenericLinkService.
#define NFD_LOG_DEBUG
Definition: logger.hpp:38
ethernet::Address getEthernetAddress() const
Returns the link-layer (Ethernet) address of the interface.
std::string to_string(const V &v)
Definition: backports.hpp:67
static std::vector< shared_ptr< const Channel > > getChannelsFromMap(const ChannelMap &channelMap)
static const std::string & getId() noexcept
#define NFD_LOG_INIT(name)
Definition: logger.hpp:31
const FaceId INVALID_FACEID
indicates an invalid FaceId
Definition: face.hpp:42
shared_ptr< EthernetChannel > createChannel(const shared_ptr< const ndn::net::NetworkInterface > &localEndpoint, time::nanoseconds idleTimeout)
Create Ethernet-based channel on the specified network interface.