face: parse face_system.netdev_bound config section
refs #3521
Change-Id: I803a1651d5b44e021ec7bedb8001e216c849b9ab
diff --git a/daemon/face/face-system.cpp b/daemon/face/face-system.cpp
index 872efef..79880da 100644
--- a/daemon/face/face-system.cpp
+++ b/daemon/face/face-system.cpp
@@ -25,6 +25,7 @@
#include "face-system.hpp"
#include "protocol-factory.hpp"
+#include "netdev-bound.hpp"
#include "core/global-io.hpp"
#include "fw/face-table.hpp"
@@ -33,6 +34,9 @@
NFD_LOG_INIT(FaceSystem);
+static const std::string SECTION_GENERAL = "general";
+static const std::string SECTION_NETDEVBOUND = "netdev_bound";
+
FaceSystem::FaceSystem(FaceTable& faceTable, shared_ptr<ndn::net::NetworkMonitor> netmon)
: m_faceTable(faceTable)
, m_netmon(std::move(netmon))
@@ -42,6 +46,8 @@
NFD_LOG_TRACE("creating factory " << id);
m_factories[id] = ProtocolFactory::create(id, pfCtorParams);
}
+
+ m_netdevBound = make_unique<NetdevBound>(pfCtorParams, *this);
}
ProtocolFactoryCtorParams
@@ -77,6 +83,12 @@
return found == m_factoryByScheme.end() ? nullptr : found->second;
}
+bool
+FaceSystem::hasFactoryForScheme(const std::string& scheme) const
+{
+ return m_factoryByScheme.count(scheme) > 0;
+}
+
void
FaceSystem::setConfigFile(ConfigFile& configFile)
{
@@ -90,7 +102,7 @@
context.isDryRun = isDryRun;
// process general protocol factory config section
- auto generalSection = configSection.get_child_optional("general");
+ auto generalSection = configSection.get_child_optional(SECTION_GENERAL);
if (generalSection) {
for (const auto& pair : *generalSection) {
const std::string& key = pair.first;
@@ -103,7 +115,7 @@
}
}
- // process sections in protocol factories
+ // process in protocol factories
for (const auto& pair : m_factories) {
const std::string& sectionName = pair.first;
ProtocolFactory* factory = pair.second.get();
@@ -127,6 +139,10 @@
}
}
+ // process netdev_bound section, after factories start providing *+dev schemes
+ auto netdevBoundSection = configSection.get_child_optional(SECTION_NETDEVBOUND);
+ m_netdevBound->processConfig(netdevBoundSection, context);
+
// process other sections
std::set<std::string> seenSections;
for (const auto& pair : configSection) {
@@ -137,12 +153,11 @@
BOOST_THROW_EXCEPTION(ConfigFile::Error("Duplicate section face_system." + sectionName));
}
- if (sectionName == "general" || m_factories.count(sectionName) > 0) {
+ if (sectionName == SECTION_GENERAL || sectionName == SECTION_NETDEVBOUND ||
+ m_factories.count(sectionName) > 0) {
continue;
}
- ///\todo #3521 nicfaces
-
BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system." + sectionName));
}
}
diff --git a/daemon/face/face-system.hpp b/daemon/face/face-system.hpp
index 18da37e..833da1b 100644
--- a/daemon/face/face-system.hpp
+++ b/daemon/face/face-system.hpp
@@ -39,6 +39,7 @@
namespace face {
+class NetdevBound;
class ProtocolFactory;
struct ProtocolFactoryCtorParams;
@@ -69,6 +70,9 @@
ProtocolFactory*
getFactoryByScheme(const std::string& scheme);
+ bool
+ hasFactoryForScheme(const std::string& scheme) const;
+
FaceTable&
getFaceTable()
{
@@ -109,6 +113,7 @@
/** \brief config section name => protocol factory
*/
std::map<std::string, unique_ptr<ProtocolFactory>> m_factories;
+ unique_ptr<NetdevBound> m_netdevBound;
private:
/** \brief scheme => protocol factory
@@ -118,7 +123,6 @@
std::map<std::string, ProtocolFactory*> m_factoryByScheme;
FaceTable& m_faceTable;
-
shared_ptr<ndn::net::NetworkMonitor> m_netmon;
};
diff --git a/daemon/face/netdev-bound.cpp b/daemon/face/netdev-bound.cpp
new file mode 100644
index 0000000..a47bd43
--- /dev/null
+++ b/daemon/face/netdev-bound.cpp
@@ -0,0 +1,150 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, 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 "netdev-bound.hpp"
+#include "face-system.hpp"
+#include "core/logger.hpp"
+
+namespace nfd {
+namespace face {
+
+NFD_LOG_INIT(NetdevBound);
+
+NetdevBound::NetdevBound(const ProtocolFactoryCtorParams& params, const FaceSystem& faceSystem)
+ : m_faceSystem(faceSystem)
+ , m_addFace(params.addFace)
+ , m_netmon(params.netmon)
+{
+}
+
+void
+NetdevBound::processConfig(OptionalConfigSection configSection,
+ FaceSystem::ConfigContext& context)
+{
+ std::vector<Rule> rules;
+ if (configSection) {
+ int ruleIndex = 0;
+ for (const auto& pair : *configSection) {
+ const std::string& key = pair.first;
+ const ConfigSection& value = pair.second;
+ if (key == "rule") {
+ rules.push_back(parseRule(ruleIndex++, value));
+ }
+ else {
+ BOOST_THROW_EXCEPTION(ConfigFile::Error(
+ "Unrecognized option face_system.netdev_bound." + key));
+ }
+ }
+ }
+
+ if (context.isDryRun) {
+ return;
+ }
+
+ ///\todo #3521 this should be verified in dry-run, but PFs won't publish their *+dev schemes
+ /// in dry-run
+ for (size_t i = 0; i < rules.size(); ++i) {
+ const Rule& rule = rules[i];
+ for (const FaceUri& remote : rule.remotes) {
+ std::string devScheme = remote.getScheme() + "+dev";
+ if (!m_faceSystem.hasFactoryForScheme(devScheme)) {
+ BOOST_THROW_EXCEPTION(RuleParseError(
+ i, "scheme " + devScheme + " for " + remote.toString() + " is unavailable"));
+ }
+ }
+ }
+ NFD_LOG_DEBUG("processConfig: processed " << rules.size() << " rules");
+
+ m_rules.swap(rules);
+ std::map<Key, shared_ptr<Face>> oldFaces;
+ oldFaces.swap(m_faces);
+
+ ///\todo #3521 for each face needed under m_rules:
+ /// if it's in oldFaces, add to m_faces and remove from oldFaces;
+ /// otherwise, create via factory and add to m_faces
+
+ ///\todo #3521 close faces in oldFaces
+}
+
+NetdevBound::Rule
+NetdevBound::parseRule(int index, const ConfigSection& confRule) const
+{
+ Rule rule;
+
+ bool hasWhitelist = false;
+ bool hasBlacklist = false;
+ for (const auto& pair : confRule) {
+ const std::string& key = pair.first;
+ const ConfigSection& value = pair.second;
+ if (key == "remote") {
+ try {
+ rule.remotes.emplace_back(value.get_value<std::string>());
+ }
+ catch (const FaceUri::Error& ex) {
+ BOOST_THROW_EXCEPTION(RuleParseError(index, "invalid remote FaceUri", ex.what()));
+ }
+ if (!rule.remotes.back().isCanonical()) {
+ BOOST_THROW_EXCEPTION(RuleParseError(index, "remote FaceUri is not canonical"));
+ }
+ }
+ else if (key == "whitelist") {
+ if (hasWhitelist) {
+ BOOST_THROW_EXCEPTION(RuleParseError(index, "duplicate whitelist"));
+ }
+ try {
+ rule.netifPredicate.parseWhitelist(value);
+ }
+ catch (const ConfigFile::Error& ex) {
+ BOOST_THROW_EXCEPTION(RuleParseError(index, "invalid whitelist", ex.what()));
+ }
+ hasWhitelist = true;
+ }
+ else if (key == "blacklist") {
+ if (hasBlacklist) {
+ BOOST_THROW_EXCEPTION(RuleParseError(index, "duplicate blacklist"));
+ }
+ try {
+ rule.netifPredicate.parseBlacklist(value);
+ }
+ catch (const ConfigFile::Error& ex) {
+ BOOST_THROW_EXCEPTION(RuleParseError(index, "invalid blacklist", ex.what()));
+ }
+ hasBlacklist = true;
+ }
+ else {
+ BOOST_THROW_EXCEPTION(RuleParseError(index, "unrecognized option " + key));
+ }
+ }
+
+ if (rule.remotes.empty()) {
+ BOOST_THROW_EXCEPTION(RuleParseError(index, "remote FaceUri is missing"));
+ }
+
+ ///\todo #3521 for each remote, check that there is a factory providing scheme+dev scheme
+ return rule;
+}
+
+} // namespace face
+} // namespace nfd
diff --git a/daemon/face/netdev-bound.hpp b/daemon/face/netdev-bound.hpp
new file mode 100644
index 0000000..17a6ce8
--- /dev/null
+++ b/daemon/face/netdev-bound.hpp
@@ -0,0 +1,93 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, 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_NETDEV_BOUND_HPP
+#define NFD_DAEMON_FACE_NETDEV_BOUND_HPP
+
+#include "protocol-factory.hpp"
+
+namespace nfd {
+namespace face {
+
+class FaceSystem;
+
+/** \brief manages netdev-bound faces
+ */
+class NetdevBound : noncopyable
+{
+public:
+ class RuleParseError : public ConfigFile::Error
+ {
+ public:
+ RuleParseError(int index, std::string msg)
+ : Error("Error parsing face_system.netdev_bound.rule[" + to_string(index) + "]: " + msg)
+ , index(index)
+ , msg(std::move(msg))
+ {
+ }
+
+ RuleParseError(int index, std::string msg, const char* innerMsg)
+ : RuleParseError(index, msg + " - " + innerMsg)
+ {
+ }
+
+ public:
+ int index;
+ std::string msg;
+ };
+
+ NetdevBound(const ProtocolFactoryCtorParams& params, const FaceSystem& faceSystem);
+
+ /** \brief process face_system.netdev_bound config section
+ */
+ void
+ processConfig(OptionalConfigSection configSection,
+ FaceSystem::ConfigContext& context);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ struct Rule
+ {
+ std::vector<FaceUri> remotes;
+ NetworkInterfacePredicate netifPredicate;
+ };
+
+ Rule
+ parseRule(int index, const ConfigSection& confRule) const;
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ const FaceSystem& m_faceSystem;
+ FaceCreatedCallback m_addFace;
+ shared_ptr<ndn::net::NetworkMonitor> m_netmon;
+
+ std::vector<Rule> m_rules;
+
+ using Key = std::pair<FaceUri, std::string>;
+ std::map<Key, shared_ptr<Face>> m_faces;
+};
+
+} // namespace face
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_NETDEV_BOUND_HPP
diff --git a/daemon/face/protocol-factory.cpp b/daemon/face/protocol-factory.cpp
index 0a8a150..fb1440d 100644
--- a/daemon/face/protocol-factory.cpp
+++ b/daemon/face/protocol-factory.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2014-2017, Regents of the University of California,
+ * Copyright (c) 2014-2018, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -66,5 +66,21 @@
BOOST_ASSERT(netmon != nullptr);
}
+shared_ptr<Face>
+ProtocolFactory::createNetdevBoundFace(const FaceUri& remote,
+ const shared_ptr<const ndn::net::NetworkInterface>& netif)
+{
+ BOOST_ASSERT(remote.isCanonical());
+ return this->doCreateNetdevBoundFace(remote, netif);
+}
+
+shared_ptr<Face>
+ProtocolFactory::doCreateNetdevBoundFace(const FaceUri& remote,
+ const shared_ptr<const ndn::net::NetworkInterface>& netif)
+{
+ BOOST_THROW_EXCEPTION(Error(
+ "this protocol factory does not support netdev-bound faces"));
+}
+
} // namespace face
} // namespace nfd
diff --git a/daemon/face/protocol-factory.hpp b/daemon/face/protocol-factory.hpp
index 74dbde7..f3d7807 100644
--- a/daemon/face/protocol-factory.hpp
+++ b/daemon/face/protocol-factory.hpp
@@ -156,6 +156,18 @@
virtual std::vector<shared_ptr<const Channel>>
getChannels() const = 0;
+ /** \brief Create a netdev-bound face
+ * \param remote remote FaceUri, must be canonical
+ * \param netdev local network interface
+ * \return new face
+ * \throw Error cannot create a face using specified arguments
+ * \note The caller must ensure there is no existing netdev-bound face with same remote FaceUri
+ * on the same local network interface.
+ */
+ shared_ptr<Face>
+ createNetdevBoundFace(const FaceUri& remote,
+ const shared_ptr<const ndn::net::NetworkInterface>& netdev);
+
protected:
explicit
ProtocolFactory(const CtorParams& params);
@@ -169,6 +181,26 @@
return channels;
}
+private:
+ /** \brief Create a netdev-bound face
+ * \sa createNetdevBoundFace
+ *
+ * The base class implementation always throws Error indicating netdev-bound faces are not
+ * supported.
+ *
+ * A subclass that offers netdev-bound faces should override this method, and also expose
+ * "scheme+dev" in providedSchemes. For example, UdpFactory should provide "udp4+dev" scheme,
+ * in addition to "udp4" scheme.
+ *
+ * The face should be constructed immediately. Face persistency shall be reported as PERMANENT.
+ * Face state shall remain DOWN until underlying transport is connected. The face shall remain
+ * open until after .close() is invoked, and survive all socket errors; in case the network
+ * interface disappears, the face shall remain DOWN until .close() is invoked.
+ */
+ virtual shared_ptr<Face>
+ doCreateNetdevBoundFace(const FaceUri& remote,
+ const shared_ptr<const ndn::net::NetworkInterface>& netif);
+
private: // registry
using CreateFunc = std::function<unique_ptr<ProtocolFactory>(const CtorParams&)>;
using Registry = std::map<std::string, CreateFunc>; // indexed by factory id