face: Ethernet{Face,ChannelFactory} implementation.

refs: #1191

Change-Id: I08b7ebfdc5d70fb494b1f23bd7b1a77cf18653a1
diff --git a/daemon/face/ethernet-channel-factory.cpp b/daemon/face/ethernet-channel-factory.cpp
new file mode 100644
index 0000000..4c47f85
--- /dev/null
+++ b/daemon/face/ethernet-channel-factory.cpp
@@ -0,0 +1,102 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "ethernet-channel-factory.hpp"
+#include "core/global-io.hpp"
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <pcap/pcap.h>
+
+namespace nfd {
+
+NFD_LOG_INIT("EthernetChannelFactory")
+
+shared_ptr<EthernetFace>
+EthernetChannelFactory::createMulticast(const ethernet::Endpoint& interface,
+                                        const ethernet::Address& address)
+{
+  std::vector<ethernet::Endpoint> ifs = findAllInterfaces();
+  if (std::find(ifs.begin(), ifs.end(), interface) == ifs.end())
+    throw Error(interface + " does not exist");
+
+  if (!address.isMulticast())
+    throw Error(address.toString() + " is not a multicast address");
+
+  shared_ptr<EthernetFace> face = findMulticast(interface, address);
+  if (face)
+    return face;
+
+  shared_ptr<boost::asio::posix::stream_descriptor> socket =
+    make_shared<boost::asio::posix::stream_descriptor>(boost::ref(getGlobalIoService()));
+
+  face = make_shared<EthernetFace>(boost::cref(socket),
+                                   boost::cref(interface),
+                                   boost::cref(address));
+  face->onFail += bind(&EthernetChannelFactory::afterFaceFailed,
+                       this, interface, address);
+  m_multicastFaces[std::make_pair(interface, address)] = face;
+
+  return face;
+}
+
+std::vector<ethernet::Endpoint>
+EthernetChannelFactory::findAllInterfaces()
+{
+  std::vector<ethernet::Endpoint> interfaces;
+  char errbuf[PCAP_ERRBUF_SIZE];
+  errbuf[0] = '\0';
+
+  pcap_if_t* alldevs;
+  if (pcap_findalldevs(&alldevs, errbuf) < 0)
+    {
+      NFD_LOG_WARN("pcap_findalldevs() failed: " << errbuf);
+      return interfaces;
+    }
+
+  for (pcap_if_t* device = alldevs; device != 0; device = device->next)
+    {
+      if ((device->flags & PCAP_IF_LOOPBACK) != 0)
+        // ignore loopback devices
+        continue;
+
+      ethernet::Endpoint interface(device->name);
+      if (interface == "any")
+        // ignore libpcap "any" pseudo-device
+        continue;
+      if (boost::starts_with(interface, "nflog") ||
+          boost::starts_with(interface, "nfqueue"))
+        // ignore Linux netfilter devices
+        continue;
+
+      // maybe add interface addresses too
+      // interface.addAddress ...
+      interfaces.push_back(interface);
+    }
+
+  pcap_freealldevs(alldevs);
+  return interfaces;
+}
+
+void
+EthernetChannelFactory::afterFaceFailed(const ethernet::Endpoint& interface,
+                                        const ethernet::Address& address)
+{
+  NFD_LOG_DEBUG("afterFaceFailed: " << interface << "/" << address);
+  m_multicastFaces.erase(std::make_pair(interface, address));
+}
+
+shared_ptr<EthernetFace>
+EthernetChannelFactory::findMulticast(const ethernet::Endpoint& interface,
+                                      const ethernet::Address& address) const
+{
+  MulticastFacesMap::const_iterator i = m_multicastFaces.find(std::make_pair(interface, address));
+  if (i != m_multicastFaces.end())
+    return i->second;
+  else
+    return shared_ptr<EthernetFace>();
+}
+
+} // namespace nfd