face: FaceSystem class
FaceSystem class is the entry point of NFD's face system. It owns
ProtocolFactory objects that are created from face_system section of
NFD configuration file. This responsibility is split from FaceManager.
This commit also adds test coverage for face_system.websocket config
section processing routine.
refs #3904
Change-Id: If9a23c23b81f03d2b76c29febb3f29e6007d595b
diff --git a/daemon/face/face-system.cpp b/daemon/face/face-system.cpp
new file mode 100644
index 0000000..0d4a308
--- /dev/null
+++ b/daemon/face/face-system.cpp
@@ -0,0 +1,541 @@
+/* -*- 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.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "face-system.hpp"
+#include "core/logger.hpp"
+#include "core/network-interface.hpp"
+#include "core/network-interface-predicate.hpp"
+#include "fw/face-table.hpp"
+
+// 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"
+#ifdef HAVE_UNIX_SOCKETS
+#include "unix-stream-factory.hpp"
+#endif // HAVE_UNIX_SOCKETS
+#ifdef HAVE_WEBSOCKET
+#include "websocket-factory.hpp"
+#endif // HAVE_WEBSOCKET
+
+namespace nfd {
+namespace face {
+
+NFD_LOG_INIT("FaceSystem");
+
+FaceSystem::FaceSystem(FaceTable& faceTable)
+ : m_faceTable(faceTable)
+{
+}
+
+std::set<const ProtocolFactory*>
+FaceSystem::listProtocolFactories() const
+{
+ std::set<const ProtocolFactory*> factories;
+ for (const auto& p : m_factories) {
+ factories.insert(p.second.get());
+ }
+ return factories;
+}
+
+ProtocolFactory*
+FaceSystem::getProtocolFactory(const std::string& scheme)
+{
+ auto found = m_factories.find(scheme);
+ return found == m_factories.end() ? nullptr : found->second.get();
+}
+
+void
+FaceSystem::setConfigFile(ConfigFile& configFile)
+{
+ configFile.addSectionHandler("face_system", bind(&FaceSystem::processConfig, this, _1, _2, _3));
+}
+
+void
+FaceSystem::processConfig(const ConfigSection& configSection, bool isDryRun, const std::string& filename)
+{
+ std::set<std::string> seenSections;
+ auto nicList = listNetworkInterfaces();
+
+ for (const auto& item : configSection) {
+ if (!seenSections.insert(item.first).second) {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("Duplicate \"" + item.first + "\" section"));
+ }
+
+ if (item.first == "unix") {
+ processSectionUnix(item.second, isDryRun);
+ }
+ else if (item.first == "tcp") {
+ processSectionTcp(item.second, isDryRun);
+ }
+ else if (item.first == "udp") {
+ processSectionUdp(item.second, isDryRun, nicList);
+ }
+ else if (item.first == "ether") {
+ processSectionEther(item.second, isDryRun, nicList);
+ }
+ else if (item.first == "websocket") {
+ processSectionWebSocket(item.second, isDryRun);
+ }
+ else {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" + item.first + "\""));
+ }
+ }
+}
+
+void
+FaceSystem::processSectionUnix(const ConfigSection& configSection, bool isDryRun)
+{
+ // ; the unix section contains settings of Unix stream faces and channels
+ // unix
+ // {
+ // path /var/run/nfd.sock ; Unix stream listener path
+ // }
+
+#if defined(HAVE_UNIX_SOCKETS)
+ std::string path = "/var/run/nfd.sock";
+
+ for (const auto& i : configSection) {
+ if (i.first == "path") {
+ path = i.second.get_value<std::string>();
+ }
+ else {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
+ i.first + "\" in \"unix\" section"));
+ }
+ }
+
+ if (!isDryRun) {
+ if (m_factories.count("unix") > 0) {
+ return;
+ }
+
+ auto factory = make_shared<UnixStreamFactory>();
+ m_factories.emplace("unix", factory);
+
+ auto channel = factory->createChannel(path);
+ channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
+ }
+#else
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without Unix sockets support, "
+ "cannot process \"unix\" section"));
+#endif // HAVE_UNIX_SOCKETS
+}
+
+void
+FaceSystem::processSectionTcp(const ConfigSection& configSection, bool isDryRun)
+{
+ // ; the tcp section contains settings of TCP faces and channels
+ // tcp
+ // {
+ // listen yes ; set to 'no' to disable TCP listener, default 'yes'
+ // port 6363 ; TCP listener port number
+ // }
+
+ uint16_t port = 6363;
+ bool needToListen = true;
+ bool enableV4 = true;
+ bool enableV6 = true;
+
+ for (const auto& i : configSection) {
+ if (i.first == "port") {
+ port = ConfigFile::parseNumber<uint16_t>(i, "tcp");
+ NFD_LOG_TRACE("TCP port set to " << port);
+ }
+ else if (i.first == "listen") {
+ needToListen = ConfigFile::parseYesNo(i, "tcp");
+ }
+ else if (i.first == "enable_v4") {
+ enableV4 = ConfigFile::parseYesNo(i, "tcp");
+ }
+ else if (i.first == "enable_v6") {
+ enableV6 = ConfigFile::parseYesNo(i, "tcp");
+ }
+ else {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
+ i.first + "\" in \"tcp\" section"));
+ }
+ }
+
+ if (!enableV4 && !enableV6) {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 TCP channels have been disabled."
+ " Remove \"tcp\" section to disable TCP channels or"
+ " re-enable at least one channel type."));
+ }
+
+ if (!isDryRun) {
+ if (m_factories.count("tcp") > 0) {
+ return;
+ }
+
+ auto factory = make_shared<TcpFactory>();
+ m_factories.emplace("tcp", factory);
+
+ if (enableV4) {
+ tcp::Endpoint endpoint(boost::asio::ip::tcp::v4(), port);
+ shared_ptr<TcpChannel> v4Channel = factory->createChannel(endpoint);
+ if (needToListen) {
+ v4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
+ }
+
+ m_factories.emplace("tcp4", factory);
+ }
+
+ if (enableV6) {
+ tcp::Endpoint endpoint(boost::asio::ip::tcp::v6(), port);
+ shared_ptr<TcpChannel> v6Channel = factory->createChannel(endpoint);
+ if (needToListen) {
+ v6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
+ }
+
+ m_factories.emplace("tcp6", factory);
+ }
+ }
+}
+
+void
+FaceSystem::processSectionUdp(const ConfigSection& configSection, bool isDryRun,
+ const std::vector<NetworkInterfaceInfo>& nicList)
+{
+ // ; the udp section contains settings of UDP faces and channels
+ // udp
+ // {
+ // port 6363 ; UDP unicast port number
+ // idle_timeout 600 ; idle time (seconds) before closing a UDP unicast face
+ // keep_alive_interval 25 ; interval (seconds) between keep-alive refreshes
+
+ // ; NFD creates one UDP multicast face per NIC
+ // mcast yes ; set to 'no' to disable UDP multicast, default 'yes'
+ // mcast_port 56363 ; UDP multicast port number
+ // mcast_group 224.0.23.170 ; UDP multicast group (IPv4 only)
+ // }
+
+ uint16_t port = 6363;
+ bool enableV4 = true;
+ bool enableV6 = true;
+ size_t timeout = 600;
+ size_t keepAliveInterval = 25;
+ bool useMcast = true;
+ auto mcastGroup = boost::asio::ip::address_v4::from_string("224.0.23.170");
+ uint16_t mcastPort = 56363;
+
+ for (const auto& i : configSection) {
+ if (i.first == "port") {
+ port = ConfigFile::parseNumber<uint16_t>(i, "udp");
+ NFD_LOG_TRACE("UDP unicast port set to " << port);
+ }
+ else if (i.first == "enable_v4") {
+ enableV4 = ConfigFile::parseYesNo(i, "udp");
+ }
+ else if (i.first == "enable_v6") {
+ enableV6 = ConfigFile::parseYesNo(i, "udp");
+ }
+ else if (i.first == "idle_timeout") {
+ try {
+ timeout = i.second.get_value<size_t>();
+ }
+ catch (const boost::property_tree::ptree_bad_data&) {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
+ i.first + "\" in \"udp\" section"));
+ }
+ }
+ else if (i.first == "keep_alive_interval") {
+ try {
+ keepAliveInterval = i.second.get_value<size_t>();
+ /// \todo Make use of keepAliveInterval
+ (void)(keepAliveInterval);
+ }
+ catch (const boost::property_tree::ptree_bad_data&) {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
+ i.first + "\" in \"udp\" section"));
+ }
+ }
+ else if (i.first == "mcast") {
+ useMcast = ConfigFile::parseYesNo(i, "udp");
+ }
+ else if (i.first == "mcast_port") {
+ mcastPort = ConfigFile::parseNumber<uint16_t>(i, "udp");
+ NFD_LOG_TRACE("UDP multicast port set to " << mcastPort);
+ }
+ else if (i.first == "mcast_group") {
+ boost::system::error_code ec;
+ mcastGroup = boost::asio::ip::address_v4::from_string(i.second.get_value<std::string>(), ec);
+ if (ec) {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
+ i.first + "\" in \"udp\" section"));
+ }
+ NFD_LOG_TRACE("UDP multicast group set to " << mcastGroup);
+ }
+ else {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
+ i.first + "\" in \"udp\" section"));
+ }
+ }
+
+ if (!enableV4 && !enableV6) {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 UDP channels have been disabled."
+ " Remove \"udp\" section to disable UDP channels or"
+ " re-enable at least one channel type."));
+ }
+ else if (useMcast && !enableV4) {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 multicast requested, but IPv4 channels"
+ " have been disabled (conflicting configuration options set)"));
+ }
+
+ if (!isDryRun) {
+ shared_ptr<UdpFactory> factory;
+ bool isReload = false;
+ if (m_factories.count("udp") > 0) {
+ isReload = true;
+ factory = static_pointer_cast<UdpFactory>(m_factories["udp"]);
+ }
+ else {
+ factory = make_shared<UdpFactory>();
+ m_factories.emplace("udp", factory);
+ }
+
+ if (!isReload && enableV4) {
+ udp::Endpoint endpoint(boost::asio::ip::udp::v4(), port);
+ shared_ptr<UdpChannel> v4Channel = factory->createChannel(endpoint, time::seconds(timeout));
+ v4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
+
+ m_factories.emplace("udp4", factory);
+ }
+
+ if (!isReload && enableV6) {
+ udp::Endpoint endpoint(boost::asio::ip::udp::v6(), port);
+ shared_ptr<UdpChannel> v6Channel = factory->createChannel(endpoint, time::seconds(timeout));
+ v6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
+
+ m_factories.emplace("udp6", factory);
+ }
+
+ std::set<shared_ptr<Face>> multicastFacesToRemove;
+ for (const auto& i : factory->getMulticastFaces()) {
+ multicastFacesToRemove.insert(i.second);
+ }
+
+ if (useMcast && enableV4) {
+ std::vector<NetworkInterfaceInfo> ipv4MulticastInterfaces;
+ for (const auto& nic : nicList) {
+ if (nic.isUp() && nic.isMulticastCapable() && !nic.ipv4Addresses.empty()) {
+ ipv4MulticastInterfaces.push_back(nic);
+ }
+ }
+
+ bool isNicNameNecessary = false;
+#if defined(__linux__)
+ if (ipv4MulticastInterfaces.size() > 1) {
+ // On Linux if we have more than one MulticastUdpFace
+ // we need to specify the name of the interface
+ isNicNameNecessary = true;
+ }
+#endif
+
+ udp::Endpoint mcastEndpoint(mcastGroup, mcastPort);
+ for (const auto& nic : ipv4MulticastInterfaces) {
+ udp::Endpoint localEndpoint(nic.ipv4Addresses[0], mcastPort);
+ auto newFace = factory->createMulticastFace(localEndpoint, mcastEndpoint,
+ isNicNameNecessary ? nic.name : "");
+ m_faceTable.add(newFace);
+ multicastFacesToRemove.erase(newFace);
+ }
+ }
+
+ for (const auto& face : multicastFacesToRemove) {
+ face->close();
+ }
+ }
+}
+
+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_factories.count("ether") > 0) {
+ factory = static_pointer_cast<EthernetFactory>(m_factories["ether"]);
+ }
+ else {
+ factory = make_shared<EthernetFactory>();
+ m_factories.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
+ // websocket
+ // {
+ // listen yes ; set to 'no' to disable WebSocket listener, default 'yes'
+ // port 9696 ; WebSocket listener port number
+ // enable_v4 yes ; set to 'no' to disable listening on IPv4 socket, default 'yes'
+ // enable_v6 yes ; set to 'no' to disable listening on IPv6 socket, default 'yes'
+ // }
+
+#if defined(HAVE_WEBSOCKET)
+ uint16_t port = 9696;
+ bool needToListen = true;
+ bool enableV4 = true;
+ bool enableV6 = true;
+
+ for (const auto& i : configSection) {
+ if (i.first == "port") {
+ port = ConfigFile::parseNumber<uint16_t>(i, "websocket");
+ NFD_LOG_TRACE("WebSocket port set to " << port);
+ }
+ else if (i.first == "listen") {
+ needToListen = ConfigFile::parseYesNo(i, "websocket");
+ }
+ else if (i.first == "enable_v4") {
+ enableV4 = ConfigFile::parseYesNo(i, "websocket");
+ }
+ else if (i.first == "enable_v6") {
+ enableV6 = ConfigFile::parseYesNo(i, "websocket");
+ }
+ else {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
+ i.first + "\" in \"websocket\" section"));
+ }
+ }
+
+ if (!enableV4 && !enableV6) {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 WebSocket channels have been disabled."
+ " Remove \"websocket\" section to disable WebSocket channels or"
+ " re-enable at least one channel type."));
+ }
+
+ if (!enableV4 && enableV6) {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD does not allow pure IPv6 WebSocket channel."));
+ }
+
+ if (!isDryRun) {
+ if (m_factories.count("websocket") > 0) {
+ return;
+ }
+
+ auto factory = make_shared<WebSocketFactory>();
+ m_factories.emplace("websocket", factory);
+
+ shared_ptr<WebSocketChannel> channel;
+
+ if (enableV6 && enableV4) {
+ websocket::Endpoint endpoint(boost::asio::ip::address_v6::any(), port);
+ channel = factory->createChannel(endpoint);
+
+ m_factories.emplace("websocket46", factory);
+ }
+ else if (enableV4) {
+ websocket::Endpoint endpoint(boost::asio::ip::address_v4::any(), port);
+ channel = factory->createChannel(endpoint);
+
+ m_factories.emplace("websocket4", factory);
+ }
+
+ if (channel && needToListen) {
+ channel->listen(bind(&FaceTable::add, &m_faceTable, _1));
+ }
+ }
+#else
+ BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without WebSocket, "
+ "cannot process \"websocket\" section"));
+#endif // HAVE_WEBSOCKET
+}
+
+} // namespace face
+} // namespace nfd
diff --git a/daemon/face/face-system.hpp b/daemon/face/face-system.hpp
new file mode 100644
index 0000000..22b2a14
--- /dev/null
+++ b/daemon/face/face-system.hpp
@@ -0,0 +1,103 @@
+/* -*- 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.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_FACE_FACE_SYSTEM_HPP
+#define NFD_DAEMON_FACE_FACE_SYSTEM_HPP
+
+#include "core/config-file.hpp"
+#include "protocol-factory.hpp"
+
+namespace nfd {
+
+class FaceTable;
+class NetworkInterfaceInfo;
+
+namespace face {
+
+/** \brief entry point of the face system
+ *
+ * FaceSystem class is the entry point of NFD's face system.
+ * It owns ProtocolFactory objects that are created from face_system section of NFD configuration file.
+ */
+class FaceSystem : noncopyable
+{
+public:
+ explicit
+ FaceSystem(FaceTable& faceTable);
+
+ /** \return ProtocolFactory objects owned by the FaceSystem
+ */
+ std::set<const ProtocolFactory*>
+ listProtocolFactories() const;
+
+ /** \return ProtocolFactory for specified protocol scheme, or nullptr if not found
+ */
+ ProtocolFactory*
+ getProtocolFactory(const std::string& scheme);
+
+ /** \brief register handler for face_system section of NFD configuration file
+ */
+ void
+ setConfigFile(ConfigFile& configFile);
+
+private:
+ void
+ processConfig(const ConfigSection& configSection, bool isDryRun,
+ const std::string& filename);
+
+ void
+ processSectionUnix(const ConfigSection& configSection, bool isDryRun);
+
+ void
+ processSectionTcp(const ConfigSection& configSection, bool isDryRun);
+
+ void
+ processSectionUdp(const ConfigSection& configSection, bool isDryRun,
+ 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:
+ /** \brief scheme => protocol factory
+ *
+ * The same protocol factory may be available under multiple schemes.
+ */
+ std::map<std::string, shared_ptr<ProtocolFactory>> m_factories;
+
+ FaceTable& m_faceTable;
+};
+
+} // namespace face
+
+using face::FaceSystem;
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_FACE_SYSTEM_HPP
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index 8a9bb1c..e8a79eb 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -24,35 +24,20 @@
*/
#include "face-manager.hpp"
-#include "core/network-interface.hpp"
-#include "core/network-interface-predicate.hpp"
#include "face/generic-link-service.hpp"
-#include "face/tcp-factory.hpp"
-#include "face/udp-factory.hpp"
+#include "face/protocol-factory.hpp"
#include "fw/face-table.hpp"
#include <ndn-cxx/lp/tags.hpp>
#include <ndn-cxx/mgmt/nfd/channel-status.hpp>
-#ifdef HAVE_UNIX_SOCKETS
-#include "face/unix-stream-factory.hpp"
-#endif // HAVE_UNIX_SOCKETS
-
-#ifdef HAVE_LIBPCAP
-#include "face/ethernet-factory.hpp"
-#include "face/ethernet-transport.hpp"
-#endif // HAVE_LIBPCAP
-
-#ifdef HAVE_WEBSOCKET
-#include "face/websocket-factory.hpp"
-#endif // HAVE_WEBSOCKET
-
namespace nfd {
NFD_LOG_INIT("FaceManager");
FaceManager::FaceManager(FaceTable& faceTable, Dispatcher& dispatcher, CommandAuthenticator& authenticator)
: NfdManagerBase(dispatcher, authenticator, "faces")
+ , m_faceSystem(faceTable)
, m_faceTable(faceTable)
{
registerCommandHandler<ndn::nfd::FaceCreateCommand>("create",
@@ -87,7 +72,7 @@
void
FaceManager::setConfigFile(ConfigFile& configFile)
{
- configFile.addSectionHandler("face_system", bind(&FaceManager::processConfig, this, _1, _2, _3));
+ m_faceSystem.setConfigFile(configFile);
}
void
@@ -108,22 +93,20 @@
return;
}
- auto factory = m_factories.find(uri.getScheme());
- if (factory == m_factories.end()) {
+ ProtocolFactory* factory = m_faceSystem.getProtocolFactory(uri.getScheme());
+ if (factory == nullptr) {
NFD_LOG_TRACE("received create request for unsupported protocol");
done(ControlResponse(406, "Unsupported protocol"));
return;
}
try {
- factory->second->createFace(uri,
- parameters.getFacePersistency(),
- parameters.hasFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) ?
- parameters.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) : false,
- bind(&FaceManager::afterCreateFaceSuccess,
- this, parameters, _1, done),
- bind(&FaceManager::afterCreateFaceFailure,
- this, _1, _2, done));
+ factory->createFace(uri,
+ parameters.getFacePersistency(),
+ parameters.hasFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) &&
+ parameters.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED),
+ bind(&FaceManager::afterCreateFaceSuccess, this, parameters, _1, done),
+ bind(&FaceManager::afterCreateFaceFailure, this, _1, _2, done));
}
catch (const std::runtime_error& error) {
NFD_LOG_ERROR("Face creation failed: " << error.what());
@@ -386,22 +369,14 @@
FaceManager::listChannels(const Name& topPrefix, const Interest& interest,
ndn::mgmt::StatusDatasetContext& context)
{
- std::set<const ProtocolFactory*> seenFactories;
-
- for (const auto& kv : m_factories) {
- const ProtocolFactory* factory = kv.second.get();
- bool inserted;
- std::tie(std::ignore, inserted) = seenFactories.insert(factory);
-
- if (inserted) {
- for (const auto& channel : factory->getChannels()) {
- ndn::nfd::ChannelStatus entry;
- entry.setLocalUri(channel->getUri().toString());
- context.append(entry.wireEncode());
- }
+ std::set<const ProtocolFactory*> factories = m_faceSystem.listProtocolFactories();
+ for (const ProtocolFactory* factory : factories) {
+ for (const auto& channel : factory->getChannels()) {
+ ndn::nfd::ChannelStatus entry;
+ entry.setLocalUri(channel->getUri().toString());
+ context.append(entry.wireEncode());
}
}
-
context.end();
}
@@ -548,492 +523,4 @@
});
}
-void
-FaceManager::processConfig(const ConfigSection& configSection,
- bool isDryRun,
- const std::string& filename)
-{
- bool hasSeenUnix = false;
- bool hasSeenTcp = false;
- bool hasSeenUdp = false;
- bool hasSeenEther = false;
- bool hasSeenWebSocket = false;
- auto nicList = listNetworkInterfaces();
-
- for (const auto& item : configSection) {
- if (item.first == "unix") {
- if (hasSeenUnix) {
- BOOST_THROW_EXCEPTION(Error("Duplicate \"unix\" section"));
- }
- hasSeenUnix = true;
-
- processSectionUnix(item.second, isDryRun);
- }
- else if (item.first == "tcp") {
- if (hasSeenTcp) {
- BOOST_THROW_EXCEPTION(Error("Duplicate \"tcp\" section"));
- }
- hasSeenTcp = true;
-
- processSectionTcp(item.second, isDryRun);
- }
- else if (item.first == "udp") {
- if (hasSeenUdp) {
- BOOST_THROW_EXCEPTION(Error("Duplicate \"udp\" section"));
- }
- hasSeenUdp = true;
-
- processSectionUdp(item.second, isDryRun, nicList);
- }
- else if (item.first == "ether") {
- if (hasSeenEther) {
- BOOST_THROW_EXCEPTION(Error("Duplicate \"ether\" section"));
- }
- hasSeenEther = true;
-
- processSectionEther(item.second, isDryRun, nicList);
- }
- else if (item.first == "websocket") {
- if (hasSeenWebSocket) {
- BOOST_THROW_EXCEPTION(Error("Duplicate \"websocket\" section"));
- }
- hasSeenWebSocket = true;
-
- processSectionWebSocket(item.second, isDryRun);
- }
- else {
- BOOST_THROW_EXCEPTION(Error("Unrecognized option \"" + item.first + "\""));
- }
- }
-}
-
-void
-FaceManager::processSectionUnix(const ConfigSection& configSection, bool isDryRun)
-{
- // ; the unix section contains settings of Unix stream faces and channels
- // unix
- // {
- // path /var/run/nfd.sock ; Unix stream listener path
- // }
-
-#if defined(HAVE_UNIX_SOCKETS)
- std::string path = "/var/run/nfd.sock";
-
- for (const auto& i : configSection) {
- if (i.first == "path") {
- path = i.second.get_value<std::string>();
- }
- else {
- BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
- i.first + "\" in \"unix\" section"));
- }
- }
-
- if (!isDryRun) {
- if (m_factories.count("unix") > 0) {
- return;
- }
-
- auto factory = make_shared<UnixStreamFactory>();
- m_factories.insert(std::make_pair("unix", factory));
-
- auto channel = factory->createChannel(path);
- channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
- }
-#else
- BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without Unix sockets support, "
- "cannot process \"unix\" section"));
-#endif // HAVE_UNIX_SOCKETS
-}
-
-void
-FaceManager::processSectionTcp(const ConfigSection& configSection, bool isDryRun)
-{
- // ; the tcp section contains settings of TCP faces and channels
- // tcp
- // {
- // listen yes ; set to 'no' to disable TCP listener, default 'yes'
- // port 6363 ; TCP listener port number
- // }
-
- uint16_t port = 6363;
- bool needToListen = true;
- bool enableV4 = true;
- bool enableV6 = true;
-
- for (const auto& i : configSection) {
- if (i.first == "port") {
- port = ConfigFile::parseNumber<uint16_t>(i, "tcp");
- NFD_LOG_TRACE("TCP port set to " << port);
- }
- else if (i.first == "listen") {
- needToListen = ConfigFile::parseYesNo(i, "tcp");
- }
- else if (i.first == "enable_v4") {
- enableV4 = ConfigFile::parseYesNo(i, "tcp");
- }
- else if (i.first == "enable_v6") {
- enableV6 = ConfigFile::parseYesNo(i, "tcp");
- }
- else {
- BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
- i.first + "\" in \"tcp\" section"));
- }
- }
-
- if (!enableV4 && !enableV6) {
- BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 TCP channels have been disabled."
- " Remove \"tcp\" section to disable TCP channels or"
- " re-enable at least one channel type."));
- }
-
- if (!isDryRun) {
- if (m_factories.count("tcp") > 0) {
- return;
- }
-
- auto factory = make_shared<TcpFactory>();
- m_factories.insert(std::make_pair("tcp", factory));
-
- if (enableV4) {
- tcp::Endpoint endpoint(boost::asio::ip::tcp::v4(), port);
- shared_ptr<TcpChannel> v4Channel = factory->createChannel(endpoint);
- if (needToListen) {
- v4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
- }
-
- m_factories.insert(std::make_pair("tcp4", factory));
- }
-
- if (enableV6) {
- tcp::Endpoint endpoint(boost::asio::ip::tcp::v6(), port);
- shared_ptr<TcpChannel> v6Channel = factory->createChannel(endpoint);
- if (needToListen) {
- v6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
- }
-
- m_factories.insert(std::make_pair("tcp6", factory));
- }
- }
-}
-
-void
-FaceManager::processSectionUdp(const ConfigSection& configSection, bool isDryRun,
- const std::vector<NetworkInterfaceInfo>& nicList)
-{
- // ; the udp section contains settings of UDP faces and channels
- // udp
- // {
- // port 6363 ; UDP unicast port number
- // idle_timeout 600 ; idle time (seconds) before closing a UDP unicast face
- // keep_alive_interval 25 ; interval (seconds) between keep-alive refreshes
-
- // ; NFD creates one UDP multicast face per NIC
- // mcast yes ; set to 'no' to disable UDP multicast, default 'yes'
- // mcast_port 56363 ; UDP multicast port number
- // mcast_group 224.0.23.170 ; UDP multicast group (IPv4 only)
- // }
-
- uint16_t port = 6363;
- bool enableV4 = true;
- bool enableV6 = true;
- size_t timeout = 600;
- size_t keepAliveInterval = 25;
- bool useMcast = true;
- auto mcastGroup = boost::asio::ip::address_v4::from_string("224.0.23.170");
- uint16_t mcastPort = 56363;
-
- for (const auto& i : configSection) {
- if (i.first == "port") {
- port = ConfigFile::parseNumber<uint16_t>(i, "udp");
- NFD_LOG_TRACE("UDP unicast port set to " << port);
- }
- else if (i.first == "enable_v4") {
- enableV4 = ConfigFile::parseYesNo(i, "udp");
- }
- else if (i.first == "enable_v6") {
- enableV6 = ConfigFile::parseYesNo(i, "udp");
- }
- else if (i.first == "idle_timeout") {
- try {
- timeout = i.second.get_value<size_t>();
- }
- catch (const boost::property_tree::ptree_bad_data&) {
- BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
- i.first + "\" in \"udp\" section"));
- }
- }
- else if (i.first == "keep_alive_interval") {
- try {
- keepAliveInterval = i.second.get_value<size_t>();
- /// \todo Make use of keepAliveInterval
- (void)(keepAliveInterval);
- }
- catch (const boost::property_tree::ptree_bad_data&) {
- BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
- i.first + "\" in \"udp\" section"));
- }
- }
- else if (i.first == "mcast") {
- useMcast = ConfigFile::parseYesNo(i, "udp");
- }
- else if (i.first == "mcast_port") {
- mcastPort = ConfigFile::parseNumber<uint16_t>(i, "udp");
- NFD_LOG_TRACE("UDP multicast port set to " << mcastPort);
- }
- else if (i.first == "mcast_group") {
- boost::system::error_code ec;
- mcastGroup = boost::asio::ip::address_v4::from_string(i.second.get_value<std::string>(), ec);
- if (ec) {
- BOOST_THROW_EXCEPTION(ConfigFile::Error("Invalid value for option \"" +
- i.first + "\" in \"udp\" section"));
- }
- NFD_LOG_TRACE("UDP multicast group set to " << mcastGroup);
- }
- else {
- BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
- i.first + "\" in \"udp\" section"));
- }
- }
-
- if (!enableV4 && !enableV6) {
- BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 UDP channels have been disabled."
- " Remove \"udp\" section to disable UDP channels or"
- " re-enable at least one channel type."));
- }
- else if (useMcast && !enableV4) {
- BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 multicast requested, but IPv4 channels"
- " have been disabled (conflicting configuration options set)"));
- }
-
- if (!isDryRun) {
- shared_ptr<UdpFactory> factory;
- bool isReload = false;
- if (m_factories.count("udp") > 0) {
- isReload = true;
- factory = static_pointer_cast<UdpFactory>(m_factories["udp"]);
- }
- else {
- factory = make_shared<UdpFactory>();
- m_factories.insert(std::make_pair("udp", factory));
- }
-
- if (!isReload && enableV4) {
- udp::Endpoint endpoint(boost::asio::ip::udp::v4(), port);
- shared_ptr<UdpChannel> v4Channel = factory->createChannel(endpoint, time::seconds(timeout));
- v4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
-
- m_factories.insert(std::make_pair("udp4", factory));
- }
-
- if (!isReload && enableV6) {
- udp::Endpoint endpoint(boost::asio::ip::udp::v6(), port);
- shared_ptr<UdpChannel> v6Channel = factory->createChannel(endpoint, time::seconds(timeout));
- v6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), nullptr);
-
- m_factories.insert(std::make_pair("udp6", factory));
- }
-
- std::set<shared_ptr<Face>> multicastFacesToRemove;
- for (const auto& i : factory->getMulticastFaces()) {
- multicastFacesToRemove.insert(i.second);
- }
-
- if (useMcast && enableV4) {
- std::vector<NetworkInterfaceInfo> ipv4MulticastInterfaces;
- for (const auto& nic : nicList) {
- if (nic.isUp() && nic.isMulticastCapable() && !nic.ipv4Addresses.empty()) {
- ipv4MulticastInterfaces.push_back(nic);
- }
- }
-
- bool isNicNameNecessary = false;
-#if defined(__linux__)
- if (ipv4MulticastInterfaces.size() > 1) {
- // On Linux if we have more than one MulticastUdpFace
- // we need to specify the name of the interface
- isNicNameNecessary = true;
- }
-#endif
-
- udp::Endpoint mcastEndpoint(mcastGroup, mcastPort);
- for (const auto& nic : ipv4MulticastInterfaces) {
- udp::Endpoint localEndpoint(nic.ipv4Addresses[0], mcastPort);
- auto newFace = factory->createMulticastFace(localEndpoint, mcastEndpoint,
- isNicNameNecessary ? nic.name : "");
- m_faceTable.add(newFace);
- multicastFacesToRemove.erase(newFace);
- }
- }
-
- for (const auto& face : multicastFacesToRemove) {
- face->close();
- }
- }
-}
-
-void
-FaceManager::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_factories.count("ether") > 0) {
- factory = static_pointer_cast<EthernetFactory>(m_factories["ether"]);
- }
- else {
- factory = make_shared<EthernetFactory>();
- m_factories.insert(std::make_pair("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 face::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
-FaceManager::processSectionWebSocket(const ConfigSection& configSection, bool isDryRun)
-{
- // ; the websocket section contains settings of WebSocket faces and channels
- // websocket
- // {
- // listen yes ; set to 'no' to disable WebSocket listener, default 'yes'
- // port 9696 ; WebSocket listener port number
- // enable_v4 yes ; set to 'no' to disable listening on IPv4 socket, default 'yes'
- // enable_v6 yes ; set to 'no' to disable listening on IPv6 socket, default 'yes'
- // }
-
-#if defined(HAVE_WEBSOCKET)
- uint16_t port = 9696;
- bool needToListen = true;
- bool enableV4 = true;
- bool enableV6 = true;
-
- for (const auto& i : configSection) {
- if (i.first == "port") {
- port = ConfigFile::parseNumber<uint16_t>(i, "websocket");
- NFD_LOG_TRACE("WebSocket port set to " << port);
- }
- else if (i.first == "listen") {
- needToListen = ConfigFile::parseYesNo(i, "websocket");
- }
- else if (i.first == "enable_v4") {
- enableV4 = ConfigFile::parseYesNo(i, "websocket");
- }
- else if (i.first == "enable_v6") {
- enableV6 = ConfigFile::parseYesNo(i, "websocket");
- }
- else {
- BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option \"" +
- i.first + "\" in \"websocket\" section"));
- }
- }
-
- if (!enableV4 && !enableV6) {
- BOOST_THROW_EXCEPTION(ConfigFile::Error("IPv4 and IPv6 WebSocket channels have been disabled."
- " Remove \"websocket\" section to disable WebSocket channels or"
- " re-enable at least one channel type."));
- }
-
- if (!enableV4 && enableV6) {
- BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD does not allow pure IPv6 WebSocket channel."));
- }
-
- if (!isDryRun) {
- if (m_factories.count("websocket") > 0) {
- return;
- }
-
- auto factory = make_shared<WebSocketFactory>();
- m_factories.insert(std::make_pair("websocket", factory));
-
- shared_ptr<WebSocketChannel> channel;
-
- if (enableV6 && enableV4) {
- websocket::Endpoint endpoint(boost::asio::ip::address_v6::any(), port);
- channel = factory->createChannel(endpoint);
-
- m_factories.insert(std::make_pair("websocket46", factory));
- }
- else if (enableV4) {
- websocket::Endpoint endpoint(boost::asio::ip::address_v4::any(), port);
- channel = factory->createChannel(endpoint);
-
- m_factories.insert(std::make_pair("websocket4", factory));
- }
-
- if (channel && needToListen) {
- channel->listen(bind(&FaceTable::add, &m_faceTable, _1));
- }
- }
-#else
- BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD was compiled without WebSocket, "
- "cannot process \"websocket\" section"));
-#endif // HAVE_WEBSOCKET
-}
-
} // namespace nfd
diff --git a/daemon/mgmt/face-manager.hpp b/daemon/mgmt/face-manager.hpp
index c4a2c48..3c92e93 100644
--- a/daemon/mgmt/face-manager.hpp
+++ b/daemon/mgmt/face-manager.hpp
@@ -27,17 +27,13 @@
#define NFD_DAEMON_MGMT_FACE_MANAGER_HPP
#include "nfd-manager-base.hpp"
+#include "face/face-system.hpp"
#include <ndn-cxx/mgmt/nfd/face-status.hpp>
#include <ndn-cxx/mgmt/nfd/face-query-filter.hpp>
#include <ndn-cxx/mgmt/nfd/face-event-notification.hpp>
-#include "face/face.hpp"
namespace nfd {
-class FaceTable;
-class NetworkInterfaceInfo;
-class ProtocolFactory;
-
/**
* @brief implement the Face Management of NFD Management Protocol.
* @sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt
@@ -144,30 +140,8 @@
void
connectFaceStateChangeSignal(const Face& face);
-private: // configuration
- void
- processConfig(const ConfigSection& configSection, bool isDryRun,
- const std::string& filename);
-
- void
- processSectionUnix(const ConfigSection& configSection, bool isDryRun);
-
- void
- processSectionTcp(const ConfigSection& configSection, bool isDryRun);
-
- void
- processSectionUdp(const ConfigSection& configSection, bool isDryRun,
- 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:
- std::map<std::string /*protocol*/, shared_ptr<ProtocolFactory>> m_factories;
+ FaceSystem m_faceSystem; ///\todo #3904 accept FaceSystem& in constructor; don't own FaceSystem
FaceTable& m_faceTable;
std::map<FaceId, signal::ScopedConnection> m_faceStateChangeConn;