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/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