mgmt: add UDP IPv4 and ethernet multicast face creation to face manager

refs: #1195

Change-Id: I693772d6a5c79873c5715718a6cacd80dc8c74e1
diff --git a/daemon/main.cpp b/daemon/main.cpp
index e4ead64..9f7bb75 100644
--- a/daemon/main.cpp
+++ b/daemon/main.cpp
@@ -229,6 +229,7 @@
                                 g_internalFace);
 
   g_faceManager = new FaceManager(g_forwarder->getFaceTable(), g_internalFace);
+  g_faceManager->setConfigFile(config);
 
   g_localControlHeaderManager =
     new LocalControlHeaderManager(bind(&Forwarder::getFace, g_forwarder, _1),
@@ -237,8 +238,6 @@
   g_strategyChoiceManager = new StrategyChoiceManager(g_forwarder->getStrategyChoice(),
                                                       g_internalFace);
 
-  /// \todo add face manager section handler
-
   /// \todo add parsing back when there is an official default config file
   // config.parse(g_options.m_config);
 
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index 9874176..701bddd 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -7,19 +7,19 @@
 #include "face-manager.hpp"
 
 #include "core/face-uri.hpp"
+#include "core/network-interface.hpp"
 #include "fw/face-table.hpp"
 
 #include "face/tcp-factory.hpp"
 #include "face/udp-factory.hpp"
+
+#ifdef HAVE_UNIX_SOCKETS
 #include "face/unix-stream-factory.hpp"
+#endif // HAVE_UNIX_SOCKETS
+
+#ifdef HAVE_PCAP
 #include "face/ethernet-factory.hpp"
-
-#ifdef __linux__
-#include <netinet/ether.h>
-#else
-#include <net/ethernet.h>
-#endif
-
+#endif // HAVE_PCAP
 
 namespace nfd {
 
@@ -63,6 +63,11 @@
                           bind(&FaceManager::onFaceRequest, this, _2));
 }
 
+FaceManager::~FaceManager()
+{
+
+}
+
 void
 FaceManager::setConfigFile(ConfigFile& configFile)
 {
@@ -79,6 +84,8 @@
   bool hasSeenUdp = false;
   bool hasSeenEther = false;
 
+  const std::list<shared_ptr<NetworkInterfaceInfo> > nicList(listNetworkInterfaces());
+
   for (ConfigSection::const_iterator item = configSection.begin();
        item != configSection.end();
        ++item)
@@ -105,7 +112,7 @@
             throw Error("Duplicate \"udp\" section");
           hasSeenUdp = true;
 
-          processSectionUdp(item->second, isDryRun);
+          processSectionUdp(item->second, isDryRun, nicList);
         }
       else if (item->first == "ether")
         {
@@ -113,7 +120,7 @@
             throw Error("Duplicate \"ether\" section");
           hasSeenEther = true;
 
-          processSectionEther(item->second, isDryRun);
+          processSectionEther(item->second, isDryRun, nicList);
         }
       else
         {
@@ -132,6 +139,8 @@
   //   path /var/run/nfd.sock ; UNIX stream listener path
   // }
 
+#if defined(HAVE_UNIX_SOCKETS)
+
   bool needToListen = true;
   std::string path = "/var/run/nfd.sock";
 
@@ -167,6 +176,10 @@
 
       m_factories.insert(std::make_pair("unix", factory));
     }
+#else
+  throw ConfigFile::Error("NFD was compiled without UNIX sockets support, cannot process \"unix\" section");
+#endif // HAVE_UNIX_SOCKETS
+
 }
 
 void
@@ -226,7 +239,9 @@
 }
 
 void
-FaceManager::processSectionUdp(const ConfigSection& configSection, bool isDryRun)
+FaceManager::processSectionUdp(const ConfigSection& configSection,
+                               bool isDryRun,
+                               const std::list<shared_ptr<NetworkInterfaceInfo> >& nicList)
 {
   // ; the udp section contains settings of UDP faces and channels
   // udp
@@ -245,8 +260,9 @@
   size_t timeout = 30;
   size_t keepAliveInterval = 25;
   bool useMcast = true;
+  std::string mcastGroup = "224.0.23.170";
   std::string mcastPort = "56363";
-  std::string mcastGroup;
+
 
   for (ConfigSection::const_iterator i = configSection.begin();
        i != configSection.end();
@@ -319,10 +335,7 @@
     {
       shared_ptr<UdpFactory> factory = make_shared<UdpFactory>(boost::cref(port));
 
-      shared_ptr<UdpChannel> ipv4Channel =
-        factory->createChannel("0.0.0.0", port, time::seconds(timeout));
-      shared_ptr<UdpChannel> ipv6Channel =
-        factory->createChannel("::", port, time::seconds(timeout));
+      factory->createChannel("::", port, time::seconds(timeout));
 
       m_factories.insert(std::make_pair("udp", factory));
       m_factories.insert(std::make_pair("udp4", factory));
@@ -330,14 +343,67 @@
 
       if (useMcast)
         {
-          /// \todo create one multicast face per NIC
-          NFD_LOG_WARN("multicast faces are not implemented");
+          bool useEndpoint = false;
+          udp::Endpoint localEndpoint;
+
+          try
+            {
+              localEndpoint.port(boost::lexical_cast<uint16_t>(port));
+              useEndpoint = true;
+            }
+          catch (const boost::bad_lexical_cast& error)
+            {
+              NFD_LOG_DEBUG("Treating UDP port \"" << port << "\" as a service name");
+            }
+
+          for (std::list<shared_ptr<NetworkInterfaceInfo> >::const_iterator i = nicList.begin();
+               i != nicList.end();
+               ++i)
+            {
+              const shared_ptr<NetworkInterfaceInfo>& nic = *i;
+              if (nic->isUp() && nic->isMulticastCapable() && !nic->ipv4Addresses.empty())
+                {
+                  shared_ptr<MulticastUdpFace> newFace =
+                    factory->createMulticastFace(nic->ipv4Addresses[0].to_string(),
+                                                 mcastGroup, mcastPort);
+
+                  NFD_LOG_INFO("Created multicast Face ID " << newFace->getId());
+
+                  if (useEndpoint)
+                    {
+                      for (std::vector<boost::asio::ip::address_v4>::const_iterator j =
+                             nic->ipv4Addresses.begin();
+                           j != nic->ipv4Addresses.end();
+                           ++j)
+                        {
+                          localEndpoint.address(*j);
+                          factory->createChannel(localEndpoint, time::seconds(timeout));
+                        }
+                    }
+                  else
+                    {
+                      for (std::vector<boost::asio::ip::address_v4>::const_iterator j =
+                             nic->ipv4Addresses.begin();
+                           j != nic->ipv4Addresses.end();
+                           ++j)
+                        {
+                          factory->createChannel(j->to_string(), port, time::seconds(timeout));
+                        }
+                    }
+                }
+            }
+        }
+      else
+        {
+          factory->createChannel("0.0.0.0", port, time::seconds(timeout));
         }
     }
 }
 
 void
-FaceManager::processSectionEther(const ConfigSection& configSection, bool isDryRun)
+FaceManager::processSectionEther(const ConfigSection& configSection,
+                                 bool isDryRun,
+                                 const std::list<shared_ptr<NetworkInterfaceInfo> >& nicList)
 {
   // ; the ether section contains settings of Ethernet faces and channels
   // ether
@@ -347,8 +413,12 @@
   //   mcast_group 01:00:5E:00:17:AA ; Ethernet multicast group
   // }
 
+#if defined(HAVE_PCAP)
+
+  using ethernet::Address;
+
   bool useMcast = true;
-  std::string mcastGroup;
+  Address mcastGroup(ethernet::getDefaultMulticastAddress());
 
   for (ConfigSection::const_iterator i = configSection.begin();
        i != configSection.end();
@@ -361,9 +431,8 @@
 
       else if (i->first == "mcast_group")
         {
-          const std::string mcastGroup = i->second.get_value<std::string>();
-          /// \todo change to use ethernet::Address::fromString
-          if (!ether_aton(mcastGroup.c_str()))
+          mcastGroup = Address::fromString(i->second.get_value<std::string>());
+          if (mcastGroup.isNull())
             {
               throw ConfigFile::Error("Invalid value for option \"" +
                                       i->first + "\" in \"ether\" section");
@@ -382,10 +451,34 @@
 
       if (useMcast)
         {
-          /// \todo create one multicast face per NIC
-          NFD_LOG_WARN("multicast faces are not implemented");
+          for (std::list<shared_ptr<NetworkInterfaceInfo> >::const_iterator i = nicList.begin();
+               i != nicList.end();
+               ++i)
+            {
+              const shared_ptr<NetworkInterfaceInfo>& nic = *i;
+              if (nic->isUp() && nic->isMulticastCapable())
+                {
+                  try
+                    {
+                      shared_ptr<EthernetFace> newFace =
+                        factory->createMulticastFace(nic->name, mcastGroup);
+                      NFD_LOG_INFO("Created multicast Face ID " << newFace->getId());
+                    }
+                  catch (const EthernetFactory::Error& factoryError)
+                    {
+                      NFD_LOG_ERROR(factoryError.what() << ", continuing");
+                    }
+                  catch (const EthernetFace::Error& faceError)
+                    {
+                      NFD_LOG_ERROR(faceError.what() << ", continuing");
+                    }
+                }
+            }
         }
     }
+#else
+  throw ConfigFile::Error("NFD was compiled without libpcap, cannot process \"ether\" section");
+#endif // HAVE_PCAP
 }
 
 
@@ -524,9 +617,6 @@
   sendResponse(requestName, 200, "Success");
 }
 
-FaceManager::~FaceManager()
-{
 
-}
 
 } // namespace nfd
diff --git a/daemon/mgmt/face-manager.hpp b/daemon/mgmt/face-manager.hpp
index 316af2a..55e4df4 100644
--- a/daemon/mgmt/face-manager.hpp
+++ b/daemon/mgmt/face-manager.hpp
@@ -21,6 +21,7 @@
 const std::string FACE_MANAGER_PRIVILEGE = "faces";
 class FaceTable;
 class ProtocolFactory;
+class NetworkInterfaceInfo;
 
 class FaceManager : public ManagerBase
 {
@@ -37,6 +38,9 @@
   FaceManager(FaceTable& faceTable,
               shared_ptr<InternalFace> face);
 
+  virtual
+  ~FaceManager();
+
   /** \brief Subscribe to a face management section(s) for the config file
    */
   void
@@ -45,9 +49,6 @@
   void
   onFaceRequest(const Interest& request);
 
-  VIRTUAL_WITH_TESTS
-  ~FaceManager();
-
 PROTECTED_WITH_TESTS_ELSE_PRIVATE:
 
   void
@@ -84,10 +85,14 @@
   processSectionTcp(const ConfigSection& configSection, bool isDryRun);
 
   void
-  processSectionUdp(const ConfigSection& configSection, bool isDryRun);
+  processSectionUdp(const ConfigSection& configSection,
+                    bool isDryRun,
+                    const std::list<shared_ptr<NetworkInterfaceInfo> >& nicList);
 
   void
-  processSectionEther(const ConfigSection& configSection, bool isDryRun);
+  processSectionEther(const ConfigSection& configSection,
+                      bool isDryRun,
+                      const std::list<shared_ptr<NetworkInterfaceInfo> >& nicList);
 
   /** \brief parse a config option that can be either "yes" or "no"
    *  \throw ConfigFile::Error value is neither "yes" nor "no"
@@ -102,7 +107,6 @@
   typedef std::map< std::string/*protocol*/, shared_ptr<ProtocolFactory> > FactoryMap;
   FactoryMap m_factories;
   FaceTable& m_faceTable;
-  //
 
   typedef function<void(FaceManager*,
                         const Name&,
diff --git a/tests/mgmt/face-manager.cpp b/tests/mgmt/face-manager.cpp
index 9937632..e4e7954 100644
--- a/tests/mgmt/face-manager.cpp
+++ b/tests/mgmt/face-manager.cpp
@@ -323,6 +323,8 @@
   return error.what() == expectedMessage;
 }
 
+#ifdef HAVE_UNIX_SOCKETS
+
 BOOST_AUTO_TEST_CASE(TestProcessSectionUnix)
 {
   const std::string CONFIG =
@@ -383,6 +385,7 @@
                              "Unrecognized option \"hello\" in \"unix\" section"));
 }
 
+#endif // HAVE_UNIX_SOCKETS
 
 
 BOOST_AUTO_TEST_CASE(TestProcessSectionTcp)
@@ -573,8 +576,11 @@
                              "Unrecognized option \"hello\" in \"udp\" section"));
 }
 
+#ifdef HAVE_PCAP
+
 BOOST_AUTO_TEST_CASE(TestProcessSectionEther)
 {
+
   const std::string CONFIG =
     "face_system\n"
     "{\n"
@@ -651,6 +657,8 @@
                              "Unrecognized option \"hello\" in \"ether\" section"));
 }
 
+#endif
+
 BOOST_AUTO_TEST_CASE(TestFireInterestFilter)
 {
   shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/faces"));
@@ -738,23 +746,6 @@
   }
 };
 
-// template <> class AuthorizedCommandFixture<FaceManagerFixture> :
-//     public CommandFixture<FaceManagerFixture>
-// {
-// public:
-//   AuthorizedCommandFixture()
-//   {
-//     const std::string regex = "^<localhost><nfd><faces>";
-//     FaceManagerFixture::ManagerBase::addInterestRule(regex, *CommandFixture<FaceManagerFixture>::m_certificate);
-//   }
-
-//   virtual
-//   ~AuthorizedCommandFixture()
-//   {
-
-//   }
-// };
-
 BOOST_FIXTURE_TEST_CASE(UnsupportedCommand, AuthorizedCommandFixture<FaceManagerFixture>)
 {
   ndn::nfd::FaceManagementOptions options;