face: unicast support in EthernetFactory

Change-Id: I1886a87d79a7194b3320a5417404b17a7290fa5d
Refs: #4012
diff --git a/daemon/face/ethernet-factory.cpp b/daemon/face/ethernet-factory.cpp
index 44282c0..20fda6d 100644
--- a/daemon/face/ethernet-factory.cpp
+++ b/daemon/face/ethernet-factory.cpp
@@ -49,6 +49,8 @@
 {
   // ether
   // {
+  //   listen yes
+  //   idle_timeout 600
   //   mcast yes
   //   mcast_group 01:00:5E:00:17:AA
   //   mcast_ad_hoc no
@@ -61,6 +63,8 @@
   //   }
   // }
 
+  bool wantListen = true;
+  uint32_t idleTimeout = 600;
   MulticastConfig mcastConfig;
 
   if (configSection) {
@@ -71,8 +75,14 @@
       const std::string& key = pair.first;
       const ConfigSection& value = pair.second;
 
-      if (key == "mcast") {
-        mcastConfig.isEnabled = ConfigFile::parseYesNo(pair, "ether");
+      if (key == "listen") {
+        wantListen = ConfigFile::parseYesNo(pair, "face_system.ether");
+      }
+      else if (key == "idle_timeout") {
+        idleTimeout = ConfigFile::parseNumber<uint32_t>(pair, "face_system.ether");
+      }
+      else if (key == "mcast") {
+        mcastConfig.isEnabled = ConfigFile::parseYesNo(pair, "face_system.ether");
       }
       else if (key == "mcast_group") {
         const std::string& valueStr = value.get_value<std::string>();
@@ -87,7 +97,7 @@
         }
       }
       else if (key == "mcast_ad_hoc") {
-        bool wantAdHoc = ConfigFile::parseYesNo(pair, "ether");
+        bool wantAdHoc = ConfigFile::parseYesNo(pair, "face_system.ether");
         mcastConfig.linkType = wantAdHoc ? ndn::nfd::LINK_TYPE_AD_HOC : ndn::nfd::LINK_TYPE_MULTI_ACCESS;
       }
       else if (key == "whitelist") {
@@ -103,6 +113,32 @@
   }
 
   if (!context.isDryRun) {
+    if (configSection) {
+      providedSchemes.insert("ether");
+
+      // determine the interfaces on which channels should be created
+      auto netifs = context.listNetifs() |
+                    boost::adaptors::filtered([this] (const NetworkInterfaceInfo& netif) {
+                      return netif.isUp() && !netif.isLoopback();
+                    });
+
+      // create channels
+      for (const auto& netif : netifs) {
+        auto channel = this->createChannel(netif, time::seconds(idleTimeout));
+        if (wantListen && !channel->isListening()) {
+          try {
+            channel->listen(context.addFace, nullptr);
+          }
+          catch (const EthernetChannel::Error& e) {
+            NFD_LOG_WARN("Cannot listen on " << netif.name << ": " << e.what());
+          }
+        }
+      }
+    }
+    else if (!m_channels.empty()) {
+      NFD_LOG_WARN("Cannot disable dev channels after initialization");
+    }
+
     if (m_mcastConfig.isEnabled != mcastConfig.isEnabled) {
       if (mcastConfig.isEnabled) {
         NFD_LOG_INFO("enabling multicast on " << mcastConfig.group);
@@ -139,13 +175,66 @@
                             const FaceCreatedCallback& onCreated,
                             const FaceCreationFailedCallback& onFailure)
 {
-  onFailure(406, "Unsupported protocol");
+  BOOST_ASSERT(remoteUri.isCanonical());
+
+  if (!localUri || localUri->getScheme() != "dev") {
+    NFD_LOG_TRACE("Cannot create unicast Ethernet face without dev:// LocalUri");
+    onFailure(406, "Creation of unicast Ethernet faces requires a LocalUri with dev:// scheme");
+    return;
+  }
+  BOOST_ASSERT(localUri->isCanonical());
+
+  if (persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
+    NFD_LOG_TRACE("createFace does not support FACE_PERSISTENCY_ON_DEMAND");
+    onFailure(406, "Outgoing Ethernet faces do not support on-demand persistency");
+    return;
+  }
+
+  ethernet::Address remoteEndpoint(ethernet::Address::fromString(remoteUri.getHost()));
+  std::string localEndpoint(localUri->getHost());
+
+  if (remoteEndpoint.isMulticast()) {
+    NFD_LOG_TRACE("createFace does not support multicast faces");
+    onFailure(406, "Cannot create multicast Ethernet faces");
+    return;
+  }
+
+  if (wantLocalFieldsEnabled) {
+    // Ethernet faces are never local
+    NFD_LOG_TRACE("createFace cannot create non-local face with local fields enabled");
+    onFailure(406, "Local fields can only be enabled on faces with local scope");
+    return;
+  }
+
+  for (const auto& i : m_channels) {
+    if (i.first == localEndpoint) {
+      i.second->connect(remoteEndpoint, persistency, onCreated, onFailure);
+      return;
+    }
+  }
+
+  NFD_LOG_TRACE("No channels available to connect to " << remoteEndpoint);
+  onFailure(504, "No channels available to connect");
+}
+
+shared_ptr<EthernetChannel>
+EthernetFactory::createChannel(const NetworkInterfaceInfo& localEndpoint,
+                               time::nanoseconds idleTimeout)
+{
+  auto it = m_channels.find(localEndpoint.name);
+  if (it != m_channels.end())
+    return it->second;
+
+  auto channel = std::make_shared<EthernetChannel>(localEndpoint, idleTimeout);
+  m_channels[localEndpoint.name] = channel;
+
+  return channel;
 }
 
 std::vector<shared_ptr<const Channel>>
 EthernetFactory::getChannels() const
 {
-  return {};
+  return getChannelsFromMap(m_channels);
 }
 
 shared_ptr<Face>
@@ -197,8 +286,7 @@
         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");
+        NFD_LOG_WARN("Cannot create Ethernet multicast face on " << netif.name << ": " << e.what());
         continue;
       }