face: process face_system.ether config section in EthernetFactory
This commit also fixes a potential memory access error in EthernetTransport.
refs #3904
Change-Id: I08296e7c6f1039b59b2859d277fc95326af34f52
diff --git a/core/network-interface-predicate.cpp b/core/network-interface-predicate.cpp
index cf5fcd6..9c1bb11 100644
--- a/core/network-interface-predicate.cpp
+++ b/core/network-interface-predicate.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2016, Regents of the University of California,
+ * Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -95,12 +95,12 @@
}
static bool
-doesMatchRule(const NetworkInterfaceInfo& nic, const std::string& rule)
+doesMatchRule(const NetworkInterfaceInfo& netif, const std::string& rule)
{
// if '/' is in rule, this is a subnet, check if IP in subnet
if (rule.find('/') != std::string::npos) {
Network n = boost::lexical_cast<Network>(rule);
- for (const auto& addr : nic.ipv4Addresses) {
+ for (const auto& addr : netif.ipv4Addresses) {
if (n.doesContain(addr)) {
return true;
}
@@ -108,15 +108,22 @@
}
return rule == "*" ||
- nic.name == rule ||
- nic.etherAddress.toString() == rule;
+ netif.name == rule ||
+ netif.etherAddress.toString() == rule;
}
bool
-NetworkInterfacePredicate::operator()(const NetworkInterfaceInfo& nic) const
+NetworkInterfacePredicate::operator()(const NetworkInterfaceInfo& netif) const
{
- return std::any_of(m_whitelist.begin(), m_whitelist.end(), bind(&doesMatchRule, nic, _1)) &&
- std::none_of(m_blacklist.begin(), m_blacklist.end(), bind(&doesMatchRule, nic, _1));
+ return std::any_of(m_whitelist.begin(), m_whitelist.end(), bind(&doesMatchRule, netif, _1)) &&
+ std::none_of(m_blacklist.begin(), m_blacklist.end(), bind(&doesMatchRule, netif, _1));
+}
+
+bool
+NetworkInterfacePredicate::operator==(const NetworkInterfacePredicate& other) const
+{
+ return this->m_whitelist == other.m_whitelist &&
+ this->m_blacklist == other.m_blacklist;
}
} // namespace nfd
diff --git a/core/network-interface-predicate.hpp b/core/network-interface-predicate.hpp
index 4254623..f48da05 100644
--- a/core/network-interface-predicate.hpp
+++ b/core/network-interface-predicate.hpp
@@ -1,12 +1,12 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2016, Regents of the University of California,
- * Arizona Board of Regents,
- * Colorado State University,
- * University Pierre & Marie Curie, Sorbonne University,
- * Washington University in St. Louis,
- * Beijing Institute of Technology,
- * The University of Memphis
+ * Copyright (c) 2014-2017, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
@@ -41,7 +41,6 @@
* all interfaces. A NetworkInterfaceInfo is accepted if it matches any entry in the whitelist and none
* of the entries in the blacklist.
*/
-
class NetworkInterfacePredicate
{
public:
@@ -60,7 +59,16 @@
parseBlacklist(const boost::property_tree::ptree& list);
bool
- operator()(const NetworkInterfaceInfo& nic) const;
+ operator()(const NetworkInterfaceInfo& netif) const;
+
+ bool
+ operator==(const NetworkInterfacePredicate& other) const;
+
+ bool
+ operator!=(const NetworkInterfacePredicate& other) const
+ {
+ return !this->operator==(other);
+ }
private:
std::set<std::string> m_whitelist;
diff --git a/daemon/face/ethernet-factory.cpp b/daemon/face/ethernet-factory.cpp
index cfb4b79..53a0815 100644
--- a/daemon/face/ethernet-factory.cpp
+++ b/daemon/face/ethernet-factory.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2016, Regents of the University of California,
+ * Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -26,33 +26,93 @@
#include "ethernet-factory.hpp"
#include "ethernet-transport.hpp"
#include "generic-link-service.hpp"
+#include "core/logger.hpp"
+#include <boost/range/adaptors.hpp>
+#include <boost/range/algorithm/copy.hpp>
namespace nfd {
+namespace face {
-shared_ptr<Face>
-EthernetFactory::createMulticastFace(const NetworkInterfaceInfo& interface,
- const ethernet::Address& address)
+NFD_LOG_INIT("EthernetFactory");
+
+void
+EthernetFactory::processConfig(OptionalConfigSection configSection,
+ FaceSystem::ConfigContext& context)
{
- if (!address.isMulticast())
- BOOST_THROW_EXCEPTION(Error(address.toString() + " is not a multicast address"));
+ // ether
+ // {
+ // mcast yes
+ // mcast_group 01:00:5E:00:17:AA
+ // whitelist
+ // {
+ // *
+ // }
+ // blacklist
+ // {
+ // }
+ // }
- auto face = findMulticastFace(interface.name, address);
- if (face)
- return face;
+ MulticastConfig mcastConfig;
- face::GenericLinkService::Options opts;
- opts.allowFragmentation = true;
- opts.allowReassembly = true;
+ if (configSection) {
+ // face_system.ether.mcast defaults to 'yes' but only if face_system.ether section is present
+ mcastConfig.isEnabled = true;
- auto linkService = make_unique<face::GenericLinkService>(opts);
- auto transport = make_unique<face::EthernetTransport>(interface, address);
- face = make_shared<Face>(std::move(linkService), std::move(transport));
+ for (const auto& pair : *configSection) {
+ const std::string& key = pair.first;
+ const ConfigSection& value = pair.second;
- auto key = std::make_pair(interface.name, address);
- m_multicastFaces[key] = face;
- connectFaceClosedSignal(*face, [this, key] { m_multicastFaces.erase(key); });
+ if (key == "mcast") {
+ mcastConfig.isEnabled = ConfigFile::parseYesNo(pair, "ether");
+ }
+ else if (key == "mcast_group") {
+ const std::string& valueStr = value.get_value<std::string>();
+ mcastConfig.group = ethernet::Address::fromString(valueStr);
+ if (mcastConfig.group.isNull()) {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("face_system.ether.mcast_group: '" +
+ valueStr + "' cannot be parsed as an Ethernet address"));
+ }
+ else if (!mcastConfig.group.isMulticast()) {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("face_system.ether.mcast_group: '" +
+ valueStr + "' is not a multicast address"));
+ }
+ }
+ else if (key == "whitelist") {
+ mcastConfig.netifPredicate.parseWhitelist(value);
+ }
+ else if (key == "blacklist") {
+ mcastConfig.netifPredicate.parseBlacklist(value);
+ }
+ else {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system.ether." + key));
+ }
+ }
+ }
- return face;
+ if (!context.isDryRun) {
+ if (m_mcastConfig.isEnabled != mcastConfig.isEnabled) {
+ if (mcastConfig.isEnabled) {
+ NFD_LOG_INFO("enabling multicast on " << mcastConfig.group);
+ }
+ else {
+ NFD_LOG_INFO("disabling multicast");
+ }
+ }
+ else if (m_mcastConfig.group != mcastConfig.group) {
+ NFD_LOG_INFO("changing multicast group from " << m_mcastConfig.group <<
+ " to " << mcastConfig.group);
+ }
+ else if (m_mcastConfig.netifPredicate != mcastConfig.netifPredicate) {
+ NFD_LOG_INFO("changing whitelist/blacklist");
+ }
+ else {
+ // There's no configuration change, but we still need to re-apply configuration because
+ // netifs may have changed.
+ }
+
+ m_mcastConfig = mcastConfig;
+ this->applyConfig(context);
+ }
}
void
@@ -72,14 +132,75 @@
}
shared_ptr<Face>
-EthernetFactory::findMulticastFace(const std::string& interfaceName,
- const ethernet::Address& address) const
+EthernetFactory::createMulticastFace(const NetworkInterfaceInfo& netif,
+ const ethernet::Address& address)
{
- auto i = m_multicastFaces.find({interfaceName, address});
- if (i != m_multicastFaces.end())
- return i->second;
- else
- return nullptr;
+ BOOST_ASSERT(address.isMulticast());
+
+ auto key = std::make_pair(netif.name, address);
+ auto found = m_mcastFaces.find(key);
+ if (found != m_mcastFaces.end()) {
+ return found->second;
+ }
+
+ face::GenericLinkService::Options opts;
+ opts.allowFragmentation = true;
+ opts.allowReassembly = true;
+
+ auto linkService = make_unique<face::GenericLinkService>(opts);
+ auto transport = make_unique<face::EthernetTransport>(netif, address);
+ auto face = make_shared<Face>(std::move(linkService), std::move(transport));
+
+ m_mcastFaces[key] = face;
+ connectFaceClosedSignal(*face, [this, key] { m_mcastFaces.erase(key); });
+
+ return face;
}
+void
+EthernetFactory::applyConfig(const FaceSystem::ConfigContext& context)
+{
+ // collect old faces
+ std::set<shared_ptr<Face>> oldFaces;
+ boost::copy(m_mcastFaces | boost::adaptors::map_values,
+ std::inserter(oldFaces, oldFaces.end()));
+
+ if (m_mcastConfig.isEnabled) {
+ // determine interfaces on which faces should be created or retained
+ auto capableNetifs = context.listNetifs() |
+ boost::adaptors::filtered([this] (const NetworkInterfaceInfo& netif) {
+ return netif.isUp() && netif.isMulticastCapable() &&
+ m_mcastConfig.netifPredicate(netif);
+ });
+
+ // create faces
+ for (const auto& netif : capableNetifs) {
+ shared_ptr<Face> face;
+ try {
+ face = this->createMulticastFace(netif, m_mcastConfig.group);
+ }
+ catch (const EthernetTransport::Error& e) {
+ NFD_LOG_ERROR("Cannot create Ethernet multicast face on " << netif.name << ": " <<
+ e.what() << ", continuing");
+ continue;
+ }
+
+ if (face->getId() == face::INVALID_FACEID) {
+ // new face: register with forwarding
+ context.addFace(face);
+ }
+ else {
+ // existing face: don't destroy
+ oldFaces.erase(face);
+ }
+ }
+ }
+
+ // destroy old faces that are not needed in new configuration
+ for (const auto& face : oldFaces) {
+ face->close();
+ }
+}
+
+} // namespace face
} // namespace nfd
diff --git a/daemon/face/ethernet-factory.hpp b/daemon/face/ethernet-factory.hpp
index 8e11024..1dec480 100644
--- a/daemon/face/ethernet-factory.hpp
+++ b/daemon/face/ethernet-factory.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2016, Regents of the University of California,
+ * Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -27,86 +27,66 @@
#define NFD_DAEMON_FACE_ETHERNET_FACTORY_HPP
#include "protocol-factory.hpp"
-#include "core/network-interface.hpp"
namespace nfd {
+namespace face {
+/** \brief protocol factory for Ethernet
+ *
+ * Currently, only Ethernet multicast is supported.
+ */
class EthernetFactory : public ProtocolFactory
{
public:
- /**
- * \brief Exception of EthernetFactory
+ /** \brief process face_system.ether config section
*/
- class Error : public ProtocolFactory::Error
- {
- public:
- explicit
- Error(const std::string& what)
- : ProtocolFactory::Error(what)
- {
- }
- };
+ void
+ processConfig(OptionalConfigSection configSection,
+ FaceSystem::ConfigContext& context) override;
- typedef std::map<std::pair<std::string, ethernet::Address>,
- shared_ptr<Face>> MulticastFaceMap;
-
- /**
- * \brief Create an EthernetFace to communicate with the given multicast group
- *
- * If this method is called twice with the same interface and group, only
- * one face will be created. Instead, the second call will just retrieve
- * the existing face.
- *
- * \param interface Local network interface
- * \param address Ethernet broadcast/multicast destination address
- *
- * \returns always a valid shared pointer to an EthernetFace object,
- * an exception will be thrown if the creation fails
- *
- * \throws EthernetFactory::Error or EthernetTransport::Error
+ /** \brief unicast face creation is not supported and will always fail
*/
- shared_ptr<Face>
- createMulticastFace(const NetworkInterfaceInfo& interface,
- const ethernet::Address& address);
-
- /**
- * \brief Get map of configured multicast faces
- */
- const MulticastFaceMap&
- getMulticastFaces() const;
-
-public: // from ProtocolFactory
- virtual void
+ void
createFace(const FaceUri& uri,
ndn::nfd::FacePersistency persistency,
bool wantLocalFieldsEnabled,
const FaceCreatedCallback& onCreated,
const FaceCreationFailedCallback& onFailure) override;
- virtual std::vector<shared_ptr<const Channel>>
+ /** \return empty container, because Ethernet unicast is not supported
+ */
+ std::vector<shared_ptr<const Channel>>
getChannels() const override;
private:
- /**
- * \brief Look up EthernetFace using specified interface and address
- *
- * \returns shared pointer to the existing EthernetFace object
- * or nullptr when such face does not exist
+ /** \brief Create a face to communicate on the given Ethernet multicast group
+ * \param netif local network interface
+ * \param group multicast group address
+ * \note Calling this method again with same arguments returns the existing face on the given
+ * interface and multicast group rather than creating a new one.
+ * \throw EthernetTransport::Error transport creation fails
*/
shared_ptr<Face>
- findMulticastFace(const std::string& interfaceName,
- const ethernet::Address& address) const;
+ createMulticastFace(const NetworkInterfaceInfo& netif, const ethernet::Address& group);
+
+ void
+ applyConfig(const FaceSystem::ConfigContext& context);
private:
- MulticastFaceMap m_multicastFaces;
+ struct MulticastConfig
+ {
+ bool isEnabled = false;
+ ethernet::Address group = ethernet::getDefaultMulticastAddress();
+ NetworkInterfacePredicate netifPredicate;
+ };
+
+ MulticastConfig m_mcastConfig;
+
+ /// (ifname, group) => face
+ std::map<std::pair<std::string, ethernet::Address>, shared_ptr<Face>> m_mcastFaces;
};
-inline const EthernetFactory::MulticastFaceMap&
-EthernetFactory::getMulticastFaces() const
-{
- return m_multicastFaces;
-}
-
+} // namespace face
} // namespace nfd
#endif // NFD_DAEMON_FACE_ETHERNET_FACTORY_HPP
diff --git a/daemon/face/ethernet-transport.cpp b/daemon/face/ethernet-transport.cpp
index aba8b59..5ea385b 100644
--- a/daemon/face/ethernet-transport.cpp
+++ b/daemon/face/ethernet-transport.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2015, Regents of the University of California,
+ * Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -374,14 +374,15 @@
void
EthernetTransport::processErrorCode(const boost::system::error_code& error)
{
- NFD_LOG_FACE_TRACE(__func__);
-
- if (getState() == TransportState::CLOSING ||
+ // boost::asio::error::operation_aborted must be checked first. In that situation, the Transport
+ // may already have been destructed, and it's unsafe to call getState() or do logging.
+ if (error == boost::asio::error::operation_aborted ||
+ getState() == TransportState::CLOSING ||
getState() == TransportState::FAILED ||
- getState() == TransportState::CLOSED ||
- error == boost::asio::error::operation_aborted)
+ getState() == TransportState::CLOSED) {
// transport is shutting down, ignore any errors
return;
+ }
NFD_LOG_FACE_WARN("Receive operation failed: " << error.message());
}
diff --git a/daemon/face/face-system.cpp b/daemon/face/face-system.cpp
index b084220..6ce6afe 100644
--- a/daemon/face/face-system.cpp
+++ b/daemon/face/face-system.cpp
@@ -30,7 +30,6 @@
// ProtocolFactory includes, sorted alphabetically
#ifdef HAVE_LIBPCAP
#include "ethernet-factory.hpp"
-#include "ethernet-transport.hpp"
#endif // HAVE_LIBPCAP
#include "tcp-factory.hpp"
#include "udp-factory.hpp"
@@ -50,6 +49,11 @@
: m_faceTable(faceTable)
{
///\todo #3904 make a registry, and construct instances from registry
+
+#ifdef HAVE_LIBPCAP
+ m_factories["ether"] = make_shared<EthernetFactory>();
+#endif // HAVE_LIBPCAP
+
m_factories["tcp"] = make_shared<TcpFactory>();
#ifdef HAVE_UNIX_SOCKETS
@@ -93,7 +97,7 @@
ConfigContext context;
context.isDryRun = isDryRun;
context.addFace = bind(&FaceTable::add, &m_faceTable, _1);
- context.m_nicList = listNetworkInterfaces();
+ context.m_netifs = listNetworkInterfaces();
// process sections in protocol factories
for (const auto& pair : m_factories) {
@@ -132,10 +136,7 @@
///\todo #3904 process these in protocol factory
if (sectionName == "udp") {
- processSectionUdp(subSection, isDryRun, context.m_nicList);
- }
- else if (sectionName == "ether") {
- processSectionEther(subSection, isDryRun, context.m_nicList);
+ processSectionUdp(subSection, isDryRun, context.m_netifs);
}
else if (sectionName == "websocket") {
processSectionWebSocket(subSection, isDryRun);
@@ -302,89 +303,6 @@
}
void
-FaceSystem::processSectionEther(const ConfigSection& configSection, bool isDryRun,
- const std::vector<NetworkInterfaceInfo>& nicList)
-{
- // ; the ether section contains settings of Ethernet faces and channels
- // ether
- // {
- // ; NFD creates one Ethernet multicast face per NIC
- // mcast yes ; set to 'no' to disable Ethernet multicast, default 'yes'
- // mcast_group 01:00:5E:00:17:AA ; Ethernet multicast group
- // }
-
-#if defined(HAVE_LIBPCAP)
- NetworkInterfacePredicate nicPredicate;
- bool useMcast = true;
- ethernet::Address mcastGroup(ethernet::getDefaultMulticastAddress());
-
- for (const auto& i : configSection) {
- if (i.first == "mcast") {
- useMcast = ConfigFile::parseYesNo(i, "ether");
- }
- else if (i.first == "mcast_group") {
- mcastGroup = ethernet::Address::fromString(i.second.get_value<std::string>());
- if (mcastGroup.isNull()) {
- BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
- i.first + "\" in \"ether\" section"));
- }
- NFD_LOG_TRACE("Ethernet multicast group set to " << mcastGroup);
- }
- else if (i.first == "whitelist") {
- nicPredicate.parseWhitelist(i.second);
- }
- else if (i.first == "blacklist") {
- nicPredicate.parseBlacklist(i.second);
- }
- else {
- BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
- i.first + "\" in \"ether\" section"));
- }
- }
-
- if (!isDryRun) {
- shared_ptr<EthernetFactory> factory;
- if (m_factoryByScheme.count("ether") > 0) {
- factory = static_pointer_cast<EthernetFactory>(m_factoryByScheme["ether"]);
- }
- else {
- factory = make_shared<EthernetFactory>();
- m_factoryByScheme.emplace("ether", factory);
- }
-
- std::set<shared_ptr<Face>> multicastFacesToRemove;
- for (const auto& i : factory->getMulticastFaces()) {
- multicastFacesToRemove.insert(i.second);
- }
-
- if (useMcast) {
- for (const auto& nic : nicList) {
- if (nic.isUp() && nic.isMulticastCapable() && nicPredicate(nic)) {
- try {
- auto newFace = factory->createMulticastFace(nic, mcastGroup);
- m_faceTable.add(newFace);
- multicastFacesToRemove.erase(newFace);
- }
- catch (const EthernetFactory::Error& factoryError) {
- NFD_LOG_ERROR(factoryError.what() << ", continuing");
- }
- catch (const EthernetTransport::Error& faceError) {
- NFD_LOG_ERROR(faceError.what() << ", continuing");
- }
- }
- }
- }
-
- for (const auto& face : multicastFacesToRemove) {
- face->close();
- }
- }
-#else
- BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without libpcap, cannot process \"ether\" section"));
-#endif // HAVE_LIBPCAP
-}
-
-void
FaceSystem::processSectionWebSocket(const ConfigSection& configSection, bool isDryRun)
{
// ; the websocket section contains settings of WebSocket faces and channels
diff --git a/daemon/face/face-system.hpp b/daemon/face/face-system.hpp
index f5d26d3..5696aa7 100644
--- a/daemon/face/face-system.hpp
+++ b/daemon/face/face-system.hpp
@@ -76,10 +76,10 @@
{
public:
const std::vector<NetworkInterfaceInfo>&
- listNics() const
+ listNetifs() const
{
- ///\todo get NIC list from NetworkMonitor
- return m_nicList;
+ ///\todo get netifs from NetworkMonitor
+ return m_netifs;
}
public:
@@ -88,7 +88,7 @@
///\todo add NetworkMonitor
private:
- std::vector<NetworkInterfaceInfo> m_nicList;
+ std::vector<NetworkInterfaceInfo> m_netifs;
friend class FaceSystem;
};
@@ -103,10 +103,6 @@
const std::vector<NetworkInterfaceInfo>& nicList);
void
- processSectionEther(const ConfigSection& configSection, bool isDryRun,
- const std::vector<NetworkInterfaceInfo>& nicList);
-
- void
processSectionWebSocket(const ConfigSection& configSection, bool isDryRun);
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
diff --git a/tests/daemon/face/ethernet-factory.t.cpp b/tests/daemon/face/ethernet-factory.t.cpp
index 248a668..7c7f295 100644
--- a/tests/daemon/face/ethernet-factory.t.cpp
+++ b/tests/daemon/face/ethernet-factory.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2016, Regents of the University of California,
+ * Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,17 +24,275 @@
*/
#include "face/ethernet-factory.hpp"
+#include "face/face.hpp"
#include "factory-test-common.hpp"
-#include "network-interface-fixture.hpp"
+#include "ethernet-fixture.hpp"
+#include "face-system-fixture.hpp"
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/range/algorithm/count_if.hpp>
namespace nfd {
+namespace face {
namespace tests {
-using nfd::face::tests::NetworkInterfaceFixture;
+using namespace nfd::tests;
BOOST_AUTO_TEST_SUITE(Face)
-BOOST_FIXTURE_TEST_SUITE(TestEthernetFactory, NetworkInterfaceFixture)
+BOOST_FIXTURE_TEST_SUITE(TestEthernetFactory, EthernetFixture)
+
+using face::Face;
+
+class EthernetConfigFixture : public EthernetFixture
+ , public FaceSystemFixture
+{
+public:
+ std::vector<const Face*>
+ listEtherMcastFaces() const
+ {
+ return this->listFacesByScheme("ether", ndn::nfd::LINK_TYPE_MULTI_ACCESS);
+ }
+
+ size_t
+ countEtherMcastFaces() const
+ {
+ return this->listEtherMcastFaces().size();
+ }
+};
+
+BOOST_FIXTURE_TEST_SUITE(ProcessConfig, EthernetConfigFixture)
+
+BOOST_AUTO_TEST_CASE(Normal)
+{
+ SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
+
+ const std::string CONFIG = R"CONFIG(
+ face_system
+ {
+ ether
+ {
+ mcast yes
+ mcast_group 01:00:5E:00:17:AA
+ whitelist
+ {
+ *
+ }
+ blacklist
+ {
+ }
+ }
+ }
+ )CONFIG";
+
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+
+ BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), netifs.size());
+}
+
+BOOST_AUTO_TEST_CASE(Omitted)
+{
+ const std::string CONFIG = R"CONFIG(
+ face_system
+ {
+ }
+ )CONFIG";
+
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+
+ BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Whitelist)
+{
+ SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
+
+ std::string CONFIG = R"CONFIG(
+ face_system
+ {
+ ether
+ {
+ whitelist
+ {
+ ifname %ifname
+ }
+ }
+ }
+ )CONFIG";
+ boost::replace_first(CONFIG, "%ifname", netifs.front().name);
+
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+ auto etherMcastFaces = this->listEtherMcastFaces();
+ BOOST_REQUIRE_EQUAL(etherMcastFaces.size(), 1);
+ BOOST_CHECK_EQUAL(etherMcastFaces.front()->getLocalUri().getHost(), netifs.front().name);
+}
+
+BOOST_AUTO_TEST_CASE(Blacklist)
+{
+ SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
+
+ std::string CONFIG = R"CONFIG(
+ face_system
+ {
+ ether
+ {
+ blacklist
+ {
+ ifname %ifname
+ }
+ }
+ }
+ )CONFIG";
+ boost::replace_first(CONFIG, "%ifname", netifs.front().name);
+
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+ auto etherMcastFaces = this->listEtherMcastFaces();
+ BOOST_CHECK_EQUAL(etherMcastFaces.size(), netifs.size() - 1);
+ BOOST_CHECK_EQUAL(boost::count_if(etherMcastFaces, [=] (const Face* face) {
+ return face->getLocalUri().getHost() == netifs.front().name;
+ }), 0);
+}
+
+BOOST_AUTO_TEST_CASE(EnableDisableMcast)
+{
+ const std::string CONFIG_WITH_MCAST = R"CONFIG(
+ face_system
+ {
+ ether
+ {
+ mcast yes
+ }
+ }
+ )CONFIG";
+ const std::string CONFIG_WITHOUT_MCAST = R"CONFIG(
+ face_system
+ {
+ ether
+ {
+ mcast no
+ }
+ }
+ )CONFIG";
+
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITHOUT_MCAST, false));
+ BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), 0);
+
+ SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
+
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITH_MCAST, false));
+ g_io.poll();
+ BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), netifs.size());
+
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITHOUT_MCAST, false));
+ g_io.poll();
+ BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(ChangeMcastGroup)
+{
+ SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
+
+ const std::string CONFIG1 = R"CONFIG(
+ face_system
+ {
+ ether
+ {
+ mcast_group 01:00:00:00:00:01
+ }
+ }
+ )CONFIG";
+ const std::string CONFIG2 = R"CONFIG(
+ face_system
+ {
+ ether
+ {
+ mcast_group 01:00:00:00:00:02
+ }
+ }
+ )CONFIG";
+
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG1, false));
+ auto etherMcastFaces = this->listEtherMcastFaces();
+ BOOST_REQUIRE_EQUAL(etherMcastFaces.size(), netifs.size());
+ BOOST_CHECK_EQUAL(etherMcastFaces.front()->getRemoteUri(),
+ FaceUri(ethernet::Address(0x01, 0x00, 0x00, 0x00, 0x00, 0x01)));
+
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG2, false));
+ g_io.poll();
+ etherMcastFaces = this->listEtherMcastFaces();
+ BOOST_REQUIRE_EQUAL(etherMcastFaces.size(), netifs.size());
+ BOOST_CHECK_EQUAL(etherMcastFaces.front()->getRemoteUri(),
+ FaceUri(ethernet::Address(0x01, 0x00, 0x00, 0x00, 0x00, 0x02)));
+}
+
+BOOST_AUTO_TEST_CASE(BadMcast)
+{
+ const std::string CONFIG = R"CONFIG(
+ face_system
+ {
+ ether
+ {
+ mcast hello
+ }
+ }
+ )CONFIG";
+
+ BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(BadMcastGroup)
+{
+ const std::string CONFIG = R"CONFIG(
+ face_system
+ {
+ ether
+ {
+ mcast yes
+ mcast_group hello
+ }
+ }
+ )CONFIG";
+
+ BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(UnicastMcastGroup)
+{
+ const std::string CONFIG = R"CONFIG(
+ face_system
+ {
+ ether
+ {
+ mcast yes
+ mcast_group 02:00:00:00:00:01
+ }
+ }
+ )CONFIG";
+
+ BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(UnknownOption)
+{
+ const std::string CONFIG = R"CONFIG(
+ face_system
+ {
+ ether
+ {
+ hello
+ }
+ }
+ )CONFIG";
+
+ BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // ProcessConfig
BOOST_AUTO_TEST_CASE(GetChannels)
{
@@ -44,24 +302,6 @@
BOOST_CHECK_EQUAL(channels.empty(), true);
}
-BOOST_AUTO_TEST_CASE(MulticastFacesMap)
-{
- SKIP_IF_NETWORK_INTERFACE_COUNT_LT(1);
-
- EthernetFactory factory;
- auto face1 = factory.createMulticastFace(m_interfaces.front(), ethernet::getBroadcastAddress());
- auto face1bis = factory.createMulticastFace(m_interfaces.front(), ethernet::getBroadcastAddress());
- BOOST_CHECK_EQUAL(face1, face1bis);
-
- auto face2 = factory.createMulticastFace(m_interfaces.front(), ethernet::getDefaultMulticastAddress());
- BOOST_CHECK_NE(face1, face2);
-
- SKIP_IF_NETWORK_INTERFACE_COUNT_LT(2);
-
- auto face3 = factory.createMulticastFace(m_interfaces.back(), ethernet::getBroadcastAddress());
- BOOST_CHECK_NE(face1, face3);
-}
-
BOOST_AUTO_TEST_CASE(UnsupportedFaceCreate)
{
EthernetFactory factory;
@@ -89,4 +329,5 @@
BOOST_AUTO_TEST_SUITE_END() // Face
} // namespace tests
+} // namespace face
} // namespace nfd
diff --git a/tests/daemon/face/network-interface-fixture.hpp b/tests/daemon/face/ethernet-fixture.hpp
similarity index 65%
rename from tests/daemon/face/network-interface-fixture.hpp
rename to tests/daemon/face/ethernet-fixture.hpp
index bb85ee7..4228f79 100644
--- a/tests/daemon/face/network-interface-fixture.hpp
+++ b/tests/daemon/face/ethernet-fixture.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2015, Regents of the University of California,
+ * Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -23,8 +23,8 @@
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef NFD_TESTS_DAEMON_FACE_NETWORK_INTERFACE_FIXTURE_HPP
-#define NFD_TESTS_DAEMON_FACE_NETWORK_INTERFACE_FIXTURE_HPP
+#ifndef NFD_TESTS_DAEMON_FACE_ETHERNET_FIXTURE_HPP
+#define NFD_TESTS_DAEMON_FACE_ETHERNET_FIXTURE_HPP
#include "core/network-interface.hpp"
#include "face/ethernet-transport.hpp"
@@ -35,39 +35,41 @@
namespace face {
namespace tests {
-class NetworkInterfaceFixture : public nfd::tests::BaseFixture
+class EthernetFixture : public virtual nfd::tests::BaseFixture
{
protected:
- NetworkInterfaceFixture()
+ EthernetFixture()
{
for (const auto& netif : listNetworkInterfaces()) {
if (!netif.isLoopback() && netif.isUp()) {
try {
- face::EthernetTransport transport(netif, ethernet::getBroadcastAddress());
- m_interfaces.push_back(netif);
+ EthernetTransport transport(netif, ethernet::getBroadcastAddress());
+ netifs.push_back(netif);
}
- catch (const face::EthernetTransport::Error&) {
- // pass
+ catch (const EthernetTransport::Error&) {
+ // ignore
}
}
}
}
protected:
- std::vector<NetworkInterfaceInfo> m_interfaces;
+ /** \brief EthernetTransport-capable network interfaces
+ */
+ std::vector<NetworkInterfaceInfo> netifs;
};
-#define SKIP_IF_NETWORK_INTERFACE_COUNT_LT(n) \
- do { \
- if (this->m_interfaces.size() < (n)) { \
- BOOST_WARN_MESSAGE(false, "skipping assertions that require " \
- #n " or more network interfaces"); \
- return; \
- } \
+#define SKIP_IF_ETHERNET_NETIF_COUNT_LT(n) \
+ do { \
+ if (this->netifs.size() < (n)) { \
+ BOOST_WARN_MESSAGE(false, "skipping assertions that require " #n \
+ " or more EthernetTransport-capable network interfaces"); \
+ return; \
+ } \
} while (false)
} // namespace tests
} // namespace face
} // namespace nfd
-#endif // NFD_TESTS_DAEMON_FACE_NETWORK_INTERFACE_FIXTURE_HPP
+#endif // NFD_TESTS_DAEMON_FACE_ETHERNET_FIXTURE_HPP
diff --git a/tests/daemon/face/ethernet-transport.t.cpp b/tests/daemon/face/ethernet-transport.t.cpp
index f7948a2..4cc9ceb 100644
--- a/tests/daemon/face/ethernet-transport.t.cpp
+++ b/tests/daemon/face/ethernet-transport.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2015, Regents of the University of California,
+ * Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -24,9 +24,9 @@
*/
#include "face/ethernet-transport.hpp"
-#include "transport-test-common.hpp"
-#include "network-interface-fixture.hpp"
+#include "transport-test-common.hpp"
+#include "ethernet-fixture.hpp"
namespace nfd {
namespace face {
@@ -35,13 +35,13 @@
using namespace nfd::tests;
BOOST_AUTO_TEST_SUITE(Face)
-BOOST_FIXTURE_TEST_SUITE(TestEthernetTransport, NetworkInterfaceFixture)
+BOOST_FIXTURE_TEST_SUITE(TestEthernetTransport, EthernetFixture)
BOOST_AUTO_TEST_CASE(StaticProperties)
{
- SKIP_IF_NETWORK_INTERFACE_COUNT_LT(1);
+ SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
- auto netif = m_interfaces.front();
+ auto netif = netifs.front();
EthernetTransport transport(netif, ethernet::getDefaultMulticastAddress());
checkStaticPropertiesInitialized(transport);
@@ -52,9 +52,9 @@
BOOST_CHECK_EQUAL(transport.getLinkType(), ndn::nfd::LINK_TYPE_MULTI_ACCESS);
}
-// TODO add the equivalent of these test cases from ethernet.t.cpp as of commit:65caf200924b28748037750449e28bcb548dbc9c
-// SendPacket
-// ProcessIncomingPacket
+///\todo #3369 add the equivalent of these test cases from ethernet.t.cpp
+/// as of commit:65caf200924b28748037750449e28bcb548dbc9c
+/// SendPacket, ProcessIncomingPacket
BOOST_AUTO_TEST_SUITE_END() // TestEthernetTransport
BOOST_AUTO_TEST_SUITE_END() // Face
diff --git a/tests/daemon/face/face-system-fixture.hpp b/tests/daemon/face/face-system-fixture.hpp
index 6bdbb41..2198c7e 100644
--- a/tests/daemon/face/face-system-fixture.hpp
+++ b/tests/daemon/face/face-system-fixture.hpp
@@ -26,6 +26,7 @@
#ifndef NFD_TESTS_DAEMON_FACE_FACE_SYSTEM_FIXTURE_HPP
#define NFD_TESTS_DAEMON_FACE_FACE_SYSTEM_FIXTURE_HPP
+#include "face/face.hpp"
#include "face/face-system.hpp"
#include "fw/face-table.hpp"
@@ -37,7 +38,7 @@
using namespace nfd::tests;
-class FaceSystemFixture : public BaseFixture
+class FaceSystemFixture : public virtual BaseFixture
{
public:
FaceSystemFixture()
@@ -82,6 +83,26 @@
return *factory;
}
+ /** \brief list faces of specified scheme from FaceTable
+ * \param scheme local or remote FaceUri scheme
+ * \param linkType if not NONE, filter by specified LinkType
+ */
+ std::vector<const Face*>
+ listFacesByScheme(const std::string& scheme,
+ ndn::nfd::LinkType linkType = ndn::nfd::LINK_TYPE_NONE) const
+ {
+ std::vector<const Face*> faces;
+ for (const Face& face : faceTable) {
+ if ((face.getLocalUri().getScheme() == scheme ||
+ face.getRemoteUri().getScheme() == scheme) &&
+ (linkType == ndn::nfd::LINK_TYPE_NONE ||
+ face.getLinkType() == linkType)) {
+ faces.push_back(&face);
+ }
+ }
+ return faces;
+ }
+
protected:
ConfigFile configFile;
FaceTable faceTable;
diff --git a/tests/daemon/face/face-system.t.cpp b/tests/daemon/face/face-system.t.cpp
index bc398ca..826ebc4 100644
--- a/tests/daemon/face/face-system.t.cpp
+++ b/tests/daemon/face/face-system.t.cpp
@@ -27,13 +27,7 @@
#include "face-system-fixture.hpp"
// ProtocolFactory includes, sorted alphabetically
-#ifdef HAVE_LIBPCAP
-#include "face/ethernet-factory.hpp"
-#endif // HAVE_LIBPCAP
#include "face/udp-factory.hpp"
-#ifdef HAVE_UNIX_SOCKETS
-#include "face/unix-stream-factory.hpp"
-#endif // HAVE_UNIX_SOCKETS
#ifdef HAVE_WEBSOCKET
#include "face/websocket-factory.hpp"
#endif // HAVE_WEBSOCKET
@@ -409,128 +403,6 @@
}
BOOST_AUTO_TEST_SUITE_END() // ConfigUdp
-#ifdef HAVE_LIBPCAP
-BOOST_AUTO_TEST_SUITE(ConfigEther)
-
-BOOST_AUTO_TEST_CASE(Normal)
-{
- SKIP_IF_NOT_SUPERUSER();
-
- const std::string CONFIG = R"CONFIG(
- face_system
- {
- ether
- {
- mcast yes
- mcast_group 01:00:5E:00:17:AA
- whitelist
- {
- *
- }
- blacklist
- {
- }
- }
- }
- )CONFIG";
-
- BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
- BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
-
- auto& factory = this->getFactoryByScheme<EthernetFactory>("ether");
- BOOST_CHECK_EQUAL(factory.getChannels().size(), 0);
-}
-
-BOOST_AUTO_TEST_CASE(BadMcast)
-{
- const std::string CONFIG = R"CONFIG(
- face_system
- {
- ether
- {
- mcast hello
- }
- }
- )CONFIG";
-
- BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
- BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
-}
-
-BOOST_AUTO_TEST_CASE(BadMcastGroup)
-{
- const std::string CONFIG = R"CONFIG(
- face_system
- {
- ether
- {
- mcast yes
- mcast_group
- }
- }
- )CONFIG";
-
- BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
- BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
-}
-
-BOOST_AUTO_TEST_CASE(UnknownOption)
-{
- const std::string CONFIG = R"CONFIG(
- face_system
- {
- ether
- {
- hello
- }
- }
- )CONFIG";
-
- BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
- BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
-}
-
-BOOST_AUTO_TEST_CASE(MulticastReinit)
-{
- SKIP_IF_NOT_SUPERUSER();
-
- const std::string CONFIG_WITH_MCAST = R"CONFIG(
- face_system
- {
- ether
- {
- mcast yes
- }
- }
- )CONFIG";
-
- BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITH_MCAST, false));
-
- auto& factory = this->getFactoryByScheme<EthernetFactory>("ether");
-
- if (factory.getMulticastFaces().empty()) {
- BOOST_WARN_MESSAGE(false, "skipping assertions that require at least one Ethernet multicast face");
- return;
- }
-
- const std::string CONFIG_WITHOUT_MCAST = R"CONFIG(
- face_system
- {
- ether
- {
- mcast no
- }
- }
- )CONFIG";
-
- BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITHOUT_MCAST, false));
- BOOST_REQUIRE_NO_THROW(g_io.poll());
- BOOST_CHECK_EQUAL(factory.getMulticastFaces().size(), 0);
-}
-
-BOOST_AUTO_TEST_SUITE_END() // ConfigEther
-#endif // HAVE_LIBPCAP
-
#ifdef HAVE_WEBSOCKET
BOOST_AUTO_TEST_SUITE(ConfigWebSocket)