mgmt: Reinitialize multicast faces and partially reload config file on HUP signal

The following elements from the config file are reloaded:
- effective user/group
- log levels
- multicast faces (enable/disable)
- security

Change-Id: I6ddf124702b30610dd0404d8fbaa9a9d800f02bf
Refs: #1584
diff --git a/core/face-uri.hpp b/core/face-uri.hpp
index 94474c7..9173d8a 100644
--- a/core/face-uri.hpp
+++ b/core/face-uri.hpp
@@ -123,6 +123,15 @@
   std::string
   toString() const;
 
+public: // EqualityComparable concept
+  /// equality operator
+  bool
+  operator==(const FaceUri& rhs) const;
+
+  /// inequality operator
+  bool
+  operator!=(const FaceUri& rhs) const;
+
 private:
   std::string m_scheme;
   std::string m_host;
@@ -172,6 +181,22 @@
   return os.str();
 }
 
+inline bool
+FaceUri::operator==(const FaceUri& rhs) const
+{
+  return (m_scheme == rhs.m_scheme &&
+          m_host == rhs.m_host &&
+          m_isV6 == rhs.m_isV6 &&
+          m_port == rhs.m_port &&
+          m_path == rhs.m_path);
+}
+
+inline bool
+FaceUri::operator!=(const FaceUri& rhs) const
+{
+  return !(*this == rhs);
+}
+
 inline std::ostream&
 operator<<(std::ostream& os, const FaceUri& uri)
 {
diff --git a/core/logger-factory.cpp b/core/logger-factory.cpp
index 61ca866..b0ef3cd 100644
--- a/core/logger-factory.cpp
+++ b/core/logger-factory.cpp
@@ -87,6 +87,25 @@
                              level + "\"");
 }
 
+LogLevel
+LoggerFactory::extractLevel(const ConfigSection& item, const std::string& key)
+{
+  std::string levelString;
+  try
+    {
+      levelString = item.get_value<std::string>();
+    }
+  catch (const boost::property_tree::ptree_error& error)
+    {
+    }
+
+  if (levelString.empty())
+    {
+      throw LoggerFactory::Error("No logging level found for option \"" + key + "\"");
+    }
+
+  return parseLevel(levelString);
+}
 
 void
 LoggerFactory::onConfig(const ConfigSection& section,
@@ -107,33 +126,29 @@
 //   Forwarder WARN
 // }
 
-  // std::cerr << "loading logging configuration" << std::endl;
+  if (!isDryRun)
+    {
+      ConfigSection::const_assoc_iterator item = section.find("default_level");
+      if (item != section.not_found())
+        {
+          LogLevel level = extractLevel(item->second, "default_level");
+          setDefaultLevel(level);
+        }
+      else
+        {
+          setDefaultLevel(LOG_INFO);
+        }
+    }
+
   for (ConfigSection::const_iterator item = section.begin();
        item != section.end();
        ++item)
     {
-      std::string levelString;
-      try
-        {
-          levelString = item->second.get_value<std::string>();
-        }
-      catch (const boost::property_tree::ptree_error& error)
-        {
-        }
-
-      if (levelString.empty())
-        {
-          throw LoggerFactory::Error("No logging level found for option \"" + item->first + "\"");
-        }
-
-      LogLevel level = parseLevel(levelString);
+      LogLevel level = extractLevel(item->second, item->first);
 
       if (item->first == "default_level")
         {
-          if (!isDryRun)
-            {
-              setDefaultLevel(level);
-            }
+          // do nothing
         }
       else
         {
diff --git a/core/logger-factory.hpp b/core/logger-factory.hpp
index b5aa31d..cbf7424 100644
--- a/core/logger-factory.hpp
+++ b/core/logger-factory.hpp
@@ -81,6 +81,9 @@
   LogLevel
   parseLevel(const std::string& level);
 
+  LogLevel
+  extractLevel(const ConfigSection& item, const std::string& key);
+
 private:
 
   typedef std::map<std::string, LogLevel> LevelMap;
diff --git a/daemon/face/ethernet-factory.cpp b/daemon/face/ethernet-factory.cpp
index 0408f03..a399a64 100644
--- a/daemon/face/ethernet-factory.cpp
+++ b/daemon/face/ethernet-factory.cpp
@@ -69,7 +69,8 @@
 EthernetFactory::findMulticastFace(const std::string& interfaceName,
                                    const ethernet::Address& address) const
 {
-  MulticastFacesMap::const_iterator i = m_multicastFaces.find(std::make_pair(interfaceName, address));
+  MulticastFaceMap::const_iterator i =
+    m_multicastFaces.find(std::make_pair(interfaceName, address));
   if (i != m_multicastFaces.end())
     return i->second;
   else
diff --git a/daemon/face/ethernet-factory.hpp b/daemon/face/ethernet-factory.hpp
index 738164a..5630141 100644
--- a/daemon/face/ethernet-factory.hpp
+++ b/daemon/face/ethernet-factory.hpp
@@ -38,11 +38,19 @@
   /**
    * \brief Exception of EthernetFactory
    */
-  struct Error : public ProtocolFactory::Error
+  class Error : public ProtocolFactory::Error
   {
-    Error(const std::string& what) : ProtocolFactory::Error(what) {}
+  public:
+    explicit
+    Error(const std::string& what)
+      : ProtocolFactory::Error(what)
+    {
+    }
   };
 
+  typedef std::map< std::pair<std::string, ethernet::Address>,
+                    shared_ptr<EthernetFace> > MulticastFaceMap;
+
   // from ProtocolFactory
   virtual void
   createFace(const FaceUri& uri,
@@ -68,6 +76,12 @@
   createMulticastFace(const shared_ptr<NetworkInterfaceInfo>& interface,
                       const ethernet::Address& address);
 
+  /**
+   * \brief Get map of configured multicast faces
+   */
+  const MulticastFaceMap&
+  getMulticastFaces() const;
+
 private:
   void
   afterFaceFailed(const std::string& interfaceName,
@@ -86,11 +100,17 @@
                     const ethernet::Address& address) const;
 
 private:
-  typedef std::map< std::pair<std::string, ethernet::Address>,
-                    shared_ptr<EthernetFace> > MulticastFacesMap;
-  MulticastFacesMap m_multicastFaces;
+  MulticastFaceMap m_multicastFaces;
 };
 
+
+inline const EthernetFactory::MulticastFaceMap&
+EthernetFactory::getMulticastFaces() const
+{
+  return m_multicastFaces;
+}
+
+
 } // namespace nfd
 
 #endif // NFD_DAEMON_FACE_ETHERNET_FACTORY_HPP
diff --git a/daemon/face/udp-channel.hpp b/daemon/face/udp-channel.hpp
index 73403aa..4db6e43 100644
--- a/daemon/face/udp-channel.hpp
+++ b/daemon/face/udp-channel.hpp
@@ -74,6 +74,10 @@
    * \param onFaceCreated  Callback to notify successful creation of the face
    * \param onAcceptFailed Callback to notify when channel fails
    *
+   * Once a face is created, if it doesn't send/receive anything for
+   * a period of time equal to timeout, it will be destroyed
+   * \todo this functionality has to be implemented
+   *
    * \throws UdpChannel::Error if called multiple times
    */
   void
diff --git a/daemon/face/udp-factory.hpp b/daemon/face/udp-factory.hpp
index fa146ad..89cf37c 100644
--- a/daemon/face/udp-factory.hpp
+++ b/daemon/face/udp-factory.hpp
@@ -40,11 +40,18 @@
   /**
    * \brief Exception of UdpFactory
    */
-  struct Error : public ProtocolFactory::Error
+  class Error : public ProtocolFactory::Error
   {
-    Error(const std::string& what) : ProtocolFactory::Error(what) {}
+  public:
+    explicit
+    Error(const std::string& what)
+      : ProtocolFactory::Error(what)
+    {
+    }
   };
 
+  typedef std::map< udp::Endpoint, shared_ptr<MulticastUdpFace> > MulticastFaceMap;
+
   explicit
   UdpFactory(const std::string& defaultPort = "6363");
 
@@ -74,7 +81,7 @@
    */
   shared_ptr<UdpChannel>
   createChannel(const udp::Endpoint& localEndpoint,
-         const time::seconds& timeout = time::seconds(600));
+                const time::seconds& timeout = time::seconds(600));
 
   /**
    * \brief Create UDP-based channel using specified host and port number
@@ -89,17 +96,13 @@
    * Example: fe80::5e96:9dff:fe7d:9c8d%en1
    * Otherwise, you can use ::
    *
-   * Once a face is created, if it doesn't send/receive anything for
-   * a period of time equal to timeout, it will be destroyed
-   * @todo this funcionality has to be implemented
-   *
    * \throws UdpChannel::Error if the bind on the socket fails
    * \throws UdpFactory::Error
    */
   shared_ptr<UdpChannel>
   createChannel(const std::string& localHost,
-         const std::string& localPort,
-         const time::seconds& timeout = time::seconds(600));
+                const std::string& localPort,
+                const time::seconds& timeout = time::seconds(600));
 
   /**
    * \brief Create MulticastUdpFace using udp::Endpoint
@@ -147,13 +150,12 @@
              const FaceCreatedCallback& onCreated,
              const FaceConnectFailedCallback& onConnectFailed);
 
-protected:
-  typedef std::map< udp::Endpoint, shared_ptr<MulticastUdpFace> > MulticastFaceMap;
 
   /**
-   * \brief Keeps tracking of the MulticastUdpFace created
+   * \brief Get map of configured multicast faces
    */
-  MulticastFaceMap m_multicastFaces;
+  const MulticastFaceMap&
+  getMulticastFaces() const;
 
 private:
 
@@ -197,14 +199,24 @@
                                  const FaceCreatedCallback& onCreated,
                                  const FaceConnectFailedCallback& onConnectFailed);
 
+private:
   typedef std::map< udp::Endpoint, shared_ptr<UdpChannel> > ChannelMap;
+
   ChannelMap m_channels;
+  MulticastFaceMap m_multicastFaces;
 
   std::string m_defaultPort;
-
   std::set<udp::Endpoint> m_prohibitedEndpoints;
 };
 
+
+inline const UdpFactory::MulticastFaceMap&
+UdpFactory::getMulticastFaces() const
+{
+  return m_multicastFaces;
+}
+
+
 } // namespace nfd
 
 #endif // NFD_DAEMON_FACE_UDP_FACTORY_HPP
diff --git a/daemon/main.cpp b/daemon/main.cpp
index 697b65b..0e90bd0 100644
--- a/daemon/main.cpp
+++ b/daemon/main.cpp
@@ -53,28 +53,41 @@
 class Nfd : noncopyable
 {
 public:
+  explicit
+  Nfd(const std::string& configFile)
+    : m_configFile(configFile)
+    , m_originalStreamBuf(0)
+  {
+  }
+
+  ~Nfd()
+  {
+    if (static_cast<bool>(m_originalStreamBuf)) {
+      std::clog.rdbuf(m_originalStreamBuf);
+    }
+  }
 
   void
-  initialize(const std::string& configFile)
+  initialize()
   {
-    initializeLogging(configFile);
+    initializeLogging();
 
     m_forwarder = make_shared<Forwarder>();
 
-    initializeManagement(configFile);
+    initializeManagement();
 
     PrivilegeHelper::drop();
   }
 
 
   void
-  initializeLogging(const std::string& configFile)
+  initializeLogging()
   {
     ConfigFile config(&ConfigFile::ignoreUnknownSection);
     LoggerFactory::getInstance().setConfigFile(config);
 
-    config.parse(configFile, true);
-    config.parse(configFile, false);
+    config.parse(m_configFile, true);
+    config.parse(m_configFile, false);
   }
 
   class IgnoreRibAndLogSections
@@ -103,7 +116,7 @@
   };
 
   void
-  initializeManagement(const std::string& configFile)
+  initializeManagement()
   {
     m_internalFace = make_shared<InternalFace>();
 
@@ -131,8 +144,8 @@
     m_faceManager->setConfigFile(config);
 
     // parse config file
-    config.parse(configFile, true);
-    config.parse(configFile, false);
+    config.parse(m_configFile, true);
+    config.parse(m_configFile, false);
 
     // add FIB entry for NFD Management Protocol
     shared_ptr<fib::Entry> entry = m_forwarder->getFib().insert("/localhost/nfd").first;
@@ -231,12 +244,46 @@
       }
     else
       {
-        /// \todo May be try to reload config file (at least security section)
         signalSet.async_wait(bind(&Nfd::terminate, this, _1, _2, ref(signalSet)));
       }
   }
 
+  void
+  reload(const boost::system::error_code& error,
+         int signalNo,
+         boost::asio::signal_set& signalSet)
+  {
+    if (error)
+      return;
+
+    NFD_LOG_INFO("Caught signal '" << strsignal(signalNo));
+
+    ////////////////////////
+    // Reload config file //
+    ////////////////////////
+
+    // Logging
+    initializeLogging();
+    /// \todo Reopen log file
+
+    // Other stuff
+    ConfigFile config((IgnoreRibAndLogSections()));
+
+    general::setConfigFile(config);
+
+    m_internalFace->getValidator().setConfigFile(config);
+    m_faceManager->setConfigFile(config);
+
+    config.parse(m_configFile, false);
+
+    ////////////////////////
+
+    signalSet.async_wait(bind(&Nfd::reload, this, _1, _2, ref(signalSet)));
+  }
+
 private:
+  std::string m_configFile;
+
   shared_ptr<Forwarder> m_forwarder;
 
   shared_ptr<InternalFace>          m_internalFace;
@@ -244,6 +291,9 @@
   shared_ptr<FaceManager>           m_faceManager;
   shared_ptr<StrategyChoiceManager> m_strategyChoiceManager;
   shared_ptr<StatusServer>          m_statusServer;
+
+  shared_ptr<std::ofstream>         m_logFile;
+  std::basic_streambuf<char>*       m_originalStreamBuf;
 };
 
 } // namespace nfd
@@ -275,10 +325,10 @@
     return 0;
   }
 
-  Nfd nfdInstance;
+  Nfd nfdInstance(options.config);
 
   try {
-    nfdInstance.initialize(options.config);
+    nfdInstance.initialize();
   }
   catch (boost::filesystem::filesystem_error& e) {
     if (e.code() == boost::system::errc::permission_denied) {
@@ -302,16 +352,16 @@
     return 3;
   }
 
+  boost::asio::signal_set terminationSignalSet(getGlobalIoService());
+  terminationSignalSet.add(SIGINT);
+  terminationSignalSet.add(SIGTERM);
+  terminationSignalSet.async_wait(bind(&Nfd::terminate, &nfdInstance, _1, _2,
+                                       ref(terminationSignalSet)));
 
-
-
-  boost::asio::signal_set signalSet(getGlobalIoService());
-  signalSet.add(SIGINT);
-  signalSet.add(SIGTERM);
-  signalSet.add(SIGHUP);
-  signalSet.add(SIGUSR1);
-  signalSet.add(SIGUSR2);
-  signalSet.async_wait(bind(&Nfd::terminate, &nfdInstance, _1, _2, ref(signalSet)));
+  boost::asio::signal_set reloadSignalSet(getGlobalIoService());
+  reloadSignalSet.add(SIGHUP);
+  reloadSignalSet.async_wait(bind(&Nfd::reload, &nfdInstance, _1, _2,
+                                  ref(reloadSignalSet)));
 
   try {
     getGlobalIoService().run();
diff --git a/daemon/mgmt/command-validator.cpp b/daemon/mgmt/command-validator.cpp
index ea596b1..396cfe4 100644
--- a/daemon/mgmt/command-validator.cpp
+++ b/daemon/mgmt/command-validator.cpp
@@ -71,6 +71,8 @@
 
   const ConfigSection EMPTY_SECTION;
 
+  m_validator.reset();
+
   if (section.begin() == section.end())
     {
       throw ConfigFile::Error("No authorize sections found");
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index 3833dc4..e33bbe7 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -236,6 +236,19 @@
 
   if (!isDryRun)
     {
+      if (m_factories.count("unix") > 0)
+        {
+          return;
+          // shared_ptr<UnixStreamFactory> factory
+          //   = static_pointer_cast<UnixStreamFactory>(m_factories["unix"]);
+          // shared_ptr<UnixStreamChannel> unixChannel = factory->findChannel(path);
+
+          // if (static_cast<bool>(unixChannel))
+          //   {
+          //     return;
+          //   }
+        }
+
       shared_ptr<UnixStreamFactory> factory = make_shared<UnixStreamFactory>();
       shared_ptr<UnixStreamChannel> unixChannel = factory->createChannel(path);
 
@@ -249,7 +262,8 @@
       m_factories.insert(std::make_pair("unix", factory));
     }
 #else
-  throw ConfigFile::Error("NFD was compiled without UNIX sockets support, cannot process \"unix\" section");
+  throw ConfigFile::Error("NFD was compiled without UNIX sockets support, "
+                          "cannot process \"unix\" section");
 #endif // HAVE_UNIX_SOCKETS
 
 }
@@ -314,6 +328,11 @@
 
   if (!isDryRun)
     {
+      if (m_factories.count("tcp") > 0)
+        {
+          return;
+        }
+
       shared_ptr<TcpFactory> factory = ndn::make_shared<TcpFactory>(port);
       m_factories.insert(std::make_pair("tcp", factory));
 
@@ -485,10 +504,18 @@
 
   if (!isDryRun)
     {
-      shared_ptr<UdpFactory> factory = ndn::make_shared<UdpFactory>(port);
-      m_factories.insert(std::make_pair("udp", factory));
+      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 = ndn::make_shared<UdpFactory>(port);
+        m_factories.insert(std::make_pair("udp", factory));
+      }
 
-      if (enableV4)
+      if (!isReload && enableV4)
         {
           shared_ptr<UdpChannel> v4Channel =
             factory->createChannel("0.0.0.0", port, time::seconds(timeout));
@@ -499,7 +526,7 @@
           m_factories.insert(std::make_pair("udp4", factory));
         }
 
-      if (enableV6)
+      if (!isReload && enableV6)
         {
           shared_ptr<UdpChannel> v6Channel =
             factory->createChannel("::", port, time::seconds(timeout));
@@ -534,6 +561,15 @@
             }
 #endif
 
+          std::list<shared_ptr<MulticastUdpFace> > multicastFacesToRemove;
+          for (UdpFactory::MulticastFaceMap::const_iterator i =
+                 factory->getMulticastFaces().begin();
+               i != factory->getMulticastFaces().end();
+               ++i)
+            {
+              multicastFacesToRemove.push_back(i->second);
+            }
+
           for (std::list<shared_ptr<NetworkInterfaceInfo> >::const_iterator i =
                  ipv4MulticastInterfaces.begin();
                i != ipv4MulticastInterfaces.end();
@@ -547,6 +583,34 @@
                                                      isNicNameNecessary ? nic->name : "");
 
               addCreatedFaceToForwarder(newFace);
+              multicastFacesToRemove.remove(newFace);
+            }
+
+          for (std::list<shared_ptr<MulticastUdpFace> >::iterator i =
+                 multicastFacesToRemove.begin();
+               i != multicastFacesToRemove.end();
+               ++i)
+            {
+              (*i)->close();
+            }
+        }
+      else
+        {
+          std::list<shared_ptr<MulticastUdpFace> > multicastFacesToRemove;
+          for (UdpFactory::MulticastFaceMap::const_iterator i =
+                 factory->getMulticastFaces().begin();
+               i != factory->getMulticastFaces().end();
+               ++i)
+            {
+              multicastFacesToRemove.push_back(i->second);
+            }
+
+          for (std::list<shared_ptr<MulticastUdpFace> >::iterator i =
+                 multicastFacesToRemove.begin();
+               i != multicastFacesToRemove.end();
+               ++i)
+            {
+              (*i)->close();
             }
         }
     }
@@ -598,11 +662,26 @@
 
   if (!isDryRun)
     {
-      shared_ptr<EthernetFactory> factory = make_shared<EthernetFactory>();
-      m_factories.insert(std::make_pair("ether", factory));
+      shared_ptr<EthernetFactory> factory;
+      if (m_factories.count("ether") > 0) {
+        factory = static_pointer_cast<EthernetFactory>(m_factories["ether"]);
+      }
+      else {
+        factory = ndn::make_shared<EthernetFactory>();
+        m_factories.insert(std::make_pair("ether", factory));
+      }
 
       if (useMcast)
         {
+          std::list<shared_ptr<EthernetFace> > multicastFacesToRemove;
+          for (EthernetFactory::MulticastFaceMap::const_iterator i =
+                 factory->getMulticastFaces().begin();
+               i != factory->getMulticastFaces().end();
+               ++i)
+            {
+              multicastFacesToRemove.push_back(i->second);
+            }
+
           for (std::list<shared_ptr<NetworkInterfaceInfo> >::const_iterator i = nicList.begin();
                i != nicList.end();
                ++i)
@@ -616,6 +695,7 @@
                         factory->createMulticastFace(nic, mcastGroup);
 
                       addCreatedFaceToForwarder(newFace);
+                      multicastFacesToRemove.remove(newFace);
                     }
                   catch (const EthernetFactory::Error& factoryError)
                     {
@@ -627,6 +707,33 @@
                     }
                 }
             }
+
+          for (std::list<shared_ptr<EthernetFace> >::iterator i =
+                 multicastFacesToRemove.begin();
+               i != multicastFacesToRemove.end();
+               ++i)
+            {
+              (*i)->close();
+            }
+        }
+      else
+        {
+          std::list<shared_ptr<EthernetFace> > multicastFacesToRemove;
+          for (EthernetFactory::MulticastFaceMap::const_iterator i =
+                 factory->getMulticastFaces().begin();
+               i != factory->getMulticastFaces().end();
+               ++i)
+            {
+              multicastFacesToRemove.push_back(i->second);
+            }
+
+          for (std::list<shared_ptr<EthernetFace> >::iterator i =
+                 multicastFacesToRemove.begin();
+               i != multicastFacesToRemove.end();
+               ++i)
+            {
+              (*i)->close();
+            }
         }
     }
 #else
@@ -704,6 +811,11 @@
 
   if (!isDryRun)
     {
+      if (m_factories.count("websocket") > 0)
+        {
+          return;
+        }
+
       shared_ptr<WebSocketFactory> factory = ndn::make_shared<WebSocketFactory>(port);
       m_factories.insert(std::make_pair("websocket", factory));
       uint16_t portNo = boost::lexical_cast<uint16_t>(port);
@@ -743,7 +855,8 @@
   const size_t commandNComps = command.size();
   const Name::Component& verb = command.get(COMMAND_PREFIX.size());
 
-  UnsignedVerbDispatchTable::const_iterator unsignedVerbProcessor = m_unsignedVerbDispatch.find(verb);
+  UnsignedVerbDispatchTable::const_iterator unsignedVerbProcessor =
+    m_unsignedVerbDispatch.find(verb);
   if (unsignedVerbProcessor != m_unsignedVerbDispatch.end())
     {
       NFD_LOG_DEBUG("command result: processing verb: " << verb);
@@ -1014,4 +1127,15 @@
   m_statusPublisher.publish();
 }
 
+shared_ptr<ProtocolFactory>
+FaceManager::findFactory(const std::string& protocol)
+{
+  FactoryMap::iterator factory = m_factories.find(protocol);
+  if (factory != m_factories.end())
+    return factory->second;
+  else
+    return shared_ptr<ProtocolFactory>();
+}
+
+
 } // namespace nfd
diff --git a/daemon/mgmt/face-manager.hpp b/daemon/mgmt/face-manager.hpp
index 649ed4c..1040970 100644
--- a/daemon/mgmt/face-manager.hpp
+++ b/daemon/mgmt/face-manager.hpp
@@ -76,6 +76,9 @@
   void
   listFaces(const Interest& request);
 
+  shared_ptr<ProtocolFactory>
+  findFactory(const std::string& protocol);
+
 PROTECTED_WITH_TESTS_ELSE_PRIVATE:
 
   void
diff --git a/tests/core/logger.cpp b/tests/core/logger.cpp
index febadfc..9ae7fa4 100644
--- a/tests/core/logger.cpp
+++ b/tests/core/logger.cpp
@@ -493,6 +493,7 @@
   const std::string LOG_CONFIG =
     "log\n"
     "{\n"
+    "  default_level DEBUG\n"
     "  TestMadeUpModule INFO\n"
     "}\n";
 
diff --git a/tests/daemon/mgmt/face-manager.cpp b/tests/daemon/mgmt/face-manager.cpp
index e43c142..6ad6197 100644
--- a/tests/daemon/mgmt/face-manager.cpp
+++ b/tests/daemon/mgmt/face-manager.cpp
@@ -30,6 +30,8 @@
 #include "../face/dummy-face.hpp"
 #include "fw/face-table.hpp"
 #include "fw/forwarder.hpp"
+#include "face/udp-factory.hpp"
+#include "face/ethernet-factory.hpp"
 
 #include "common.hpp"
 #include "tests/test-common.hpp"
@@ -653,6 +655,37 @@
                              "Unrecognized option \"hello\" in \"udp\" section"));
 }
 
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpMulticastReinit)
+{
+  const std::string CONFIG_WITH_MCAST =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    mcast yes\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITH_MCAST, false));
+
+  shared_ptr<UdpFactory> factory = static_pointer_cast<UdpFactory>(getManager().findFactory("udp"));
+  BOOST_REQUIRE(static_cast<bool>(factory));
+
+  BOOST_CHECK_GT(factory->getMulticastFaces().size(), 0);
+
+  const std::string CONFIG_WITHOUT_MCAST =
+    "face_system\n"
+    "{\n"
+    "  udp\n"
+    "  {\n"
+    "    mcast no\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITHOUT_MCAST, false));
+  BOOST_CHECK_EQUAL(factory->getMulticastFaces().size(), 0);
+}
+
+
 #ifdef HAVE_LIBPCAP
 
 BOOST_AUTO_TEST_CASE(TestProcessSectionEther)
@@ -734,6 +767,37 @@
                              "Unrecognized option \"hello\" in \"ether\" section"));
 }
 
+BOOST_AUTO_TEST_CASE(TestProcessSectionEtherMulticastReinit)
+{
+  const std::string CONFIG_WITH_MCAST =
+    "face_system\n"
+    "{\n"
+    "  ether\n"
+    "  {\n"
+    "    mcast yes\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITH_MCAST, false));
+
+  shared_ptr<EthernetFactory> factory =
+    static_pointer_cast<EthernetFactory>(getManager().findFactory("ether"));
+  BOOST_REQUIRE(static_cast<bool>(factory));
+
+  BOOST_CHECK_GT(factory->getMulticastFaces().size(), 0);
+
+  const std::string CONFIG_WITHOUT_MCAST =
+    "face_system\n"
+    "{\n"
+    "  ether\n"
+    "  {\n"
+    "    mcast no\n"
+    "  }\n"
+    "}\n";
+  BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITHOUT_MCAST, false));
+  BOOST_CHECK_EQUAL(factory->getMulticastFaces().size(), 0);
+}
+
+
 #endif
 
 BOOST_AUTO_TEST_CASE(TestFireInterestFilter)