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