face: UdpTransport

Change-Id: I54f43248785437cdf951d50099e46e9689962a75
Refs: #3168
diff --git a/daemon/face/datagram-face.hpp b/daemon/face/datagram-face.hpp
deleted file mode 100644
index a55fb8c..0000000
--- a/daemon/face/datagram-face.hpp
+++ /dev/null
@@ -1,300 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2015,  Regents of the University of California,
- *                           Arizona Board of Regents,
- *                           Colorado State University,
- *                           University Pierre & Marie Curie, Sorbonne University,
- *                           Washington University in St. Louis,
- *                           Beijing Institute of Technology,
- *                           The University of Memphis.
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon).
- * See AUTHORS.md for complete list of NFD authors and contributors.
- *
- * NFD is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef NFD_DAEMON_FACE_DATAGRAM_FACE_HPP
-#define NFD_DAEMON_FACE_DATAGRAM_FACE_HPP
-
-#include "face.hpp"
-#include "core/global-io.hpp"
-
-namespace nfd {
-
-struct Unicast {};
-struct Multicast {};
-
-template<class Protocol, class Addressing = Unicast>
-class DatagramFace : public Face
-{
-public:
-  typedef Protocol protocol;
-
-  /** \brief Construct datagram face
-   *
-   * \param socket      Protocol-specific socket for the created face
-   */
-  DatagramFace(const FaceUri& remoteUri, const FaceUri& localUri,
-               typename protocol::socket socket);
-
-  // from Face
-  void
-  sendInterest(const Interest& interest) DECL_OVERRIDE;
-
-  void
-  sendData(const Data& data) DECL_OVERRIDE;
-
-  void
-  close() DECL_OVERRIDE;
-
-  void
-  receiveDatagram(const uint8_t* buffer,
-                  size_t nBytesReceived,
-                  const boost::system::error_code& error);
-
-protected:
-  void
-  processErrorCode(const boost::system::error_code& error);
-
-  void
-  handleSend(const boost::system::error_code& error,
-             size_t nBytesSent,
-             const Block& payload);
-
-  void
-  handleReceive(const boost::system::error_code& error,
-                size_t nBytesReceived);
-
-  void
-  keepFaceAliveUntilAllHandlersExecuted(const shared_ptr<Face>& face);
-
-  void
-  closeSocket();
-
-  bool
-  hasBeenUsedRecently() const;
-
-  /**
-   * \brief Set m_hasBeenUsedRecently to false
-   */
-  void
-  resetRecentUsage();
-
-protected:
-  typename protocol::socket m_socket;
-
-  NFD_LOG_INCLASS_DECLARE();
-
-private:
-  uint8_t m_inputBuffer[ndn::MAX_NDN_PACKET_SIZE];
-  bool m_hasBeenUsedRecently;
-};
-
-
-template<class T, class U>
-inline
-DatagramFace<T, U>::DatagramFace(const FaceUri& remoteUri, const FaceUri& localUri,
-                                 typename DatagramFace::protocol::socket socket)
-  : Face(remoteUri, localUri, false, std::is_same<U, Multicast>::value)
-  , m_socket(std::move(socket))
-{
-  NFD_LOG_FACE_INFO("Creating face");
-
-  m_socket.async_receive(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE),
-                         bind(&DatagramFace<T, U>::handleReceive, this,
-                              boost::asio::placeholders::error,
-                              boost::asio::placeholders::bytes_transferred));
-}
-
-template<class T, class U>
-inline void
-DatagramFace<T, U>::sendInterest(const Interest& interest)
-{
-  NFD_LOG_FACE_TRACE(__func__);
-
-  this->emitSignal(onSendInterest, interest);
-
-  const Block& payload = interest.wireEncode();
-  m_socket.async_send(boost::asio::buffer(payload.wire(), payload.size()),
-                      bind(&DatagramFace<T, U>::handleSend, this,
-                           boost::asio::placeholders::error,
-                           boost::asio::placeholders::bytes_transferred,
-                           payload));
-}
-
-template<class T, class U>
-inline void
-DatagramFace<T, U>::sendData(const Data& data)
-{
-  NFD_LOG_FACE_TRACE(__func__);
-
-  this->emitSignal(onSendData, data);
-
-  const Block& payload = data.wireEncode();
-  m_socket.async_send(boost::asio::buffer(payload.wire(), payload.size()),
-                      bind(&DatagramFace<T, U>::handleSend, this,
-                           boost::asio::placeholders::error,
-                           boost::asio::placeholders::bytes_transferred,
-                           payload));
-}
-
-template<class T, class U>
-inline void
-DatagramFace<T, U>::close()
-{
-  if (!m_socket.is_open())
-    return;
-
-  NFD_LOG_FACE_INFO("Closing face");
-
-  closeSocket();
-  this->fail("Face closed");
-}
-
-template<class T, class U>
-inline void
-DatagramFace<T, U>::processErrorCode(const boost::system::error_code& error)
-{
-  if (error == boost::asio::error::operation_aborted) // when cancel() is called
-    return;
-
-  if (getPersistency() == ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERMANENT) {
-    NFD_LOG_FACE_DEBUG("Permanent face ignores error: " << error.message());
-    return;
-  }
-
-  if (!m_socket.is_open()) {
-    this->fail("Tunnel closed");
-    return;
-  }
-
-  if (error != boost::asio::error::eof)
-    NFD_LOG_FACE_WARN("Send or receive operation failed: " << error.message());
-
-  closeSocket();
-
-  if (error == boost::asio::error::eof)
-    this->fail("Tunnel closed");
-  else
-    this->fail(error.message());
-}
-
-template<class T, class U>
-inline void
-DatagramFace<T, U>::handleSend(const boost::system::error_code& error,
-                               size_t nBytesSent,
-                               const Block& payload)
-// 'payload' is unused; it's needed to retain the underlying Buffer
-{
-  if (error)
-    return processErrorCode(error);
-
-  NFD_LOG_FACE_TRACE("Successfully sent: " << nBytesSent << " bytes");
-  this->getMutableCounters().getNOutBytes() += nBytesSent;
-}
-
-template<class T, class U>
-inline void
-DatagramFace<T, U>::handleReceive(const boost::system::error_code& error,
-                                  size_t nBytesReceived)
-{
-  receiveDatagram(m_inputBuffer, nBytesReceived, error);
-
-  if (m_socket.is_open())
-    m_socket.async_receive(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE),
-                           bind(&DatagramFace<T, U>::handleReceive, this,
-                                boost::asio::placeholders::error,
-                                boost::asio::placeholders::bytes_transferred));
-}
-
-template<class T, class U>
-inline void
-DatagramFace<T, U>::receiveDatagram(const uint8_t* buffer,
-                                    size_t nBytesReceived,
-                                    const boost::system::error_code& error)
-{
-  if (error)
-    return processErrorCode(error);
-
-  NFD_LOG_FACE_TRACE("Received: " << nBytesReceived << " bytes");
-  this->getMutableCounters().getNInBytes() += nBytesReceived;
-
-  bool isOk = false;
-  Block element;
-  std::tie(isOk, element) = Block::fromBuffer(buffer, nBytesReceived);
-  if (!isOk)
-    {
-      NFD_LOG_FACE_WARN("Failed to parse incoming packet");
-      // This message won't extend the face lifetime
-      return;
-    }
-
-  if (element.size() != nBytesReceived)
-    {
-      NFD_LOG_FACE_WARN("Received datagram size and decoded element size don't match");
-      // This message won't extend the face lifetime
-      return;
-    }
-
-  if (!this->decodeAndDispatchInput(element))
-    {
-      NFD_LOG_FACE_WARN("Received unrecognized TLV block of type " << element.type());
-      // This message won't extend the face lifetime
-      return;
-    }
-
-  m_hasBeenUsedRecently = true;
-}
-
-template<class T, class U>
-inline void
-DatagramFace<T, U>::keepFaceAliveUntilAllHandlersExecuted(const shared_ptr<Face>& face)
-{
-  NFD_LOG_FACE_TRACE(__func__);
-}
-
-template<class T, class U>
-inline void
-DatagramFace<T, U>::closeSocket()
-{
-  NFD_LOG_FACE_TRACE(__func__);
-
-  // use the non-throwing variants and ignore errors, if any
-  boost::system::error_code error;
-  m_socket.shutdown(protocol::socket::shutdown_both, error);
-  m_socket.close(error);
-  // after this, handlers will be called with an error code
-
-  // ensure that the Face object is alive at least until all pending
-  // handlers are dispatched
-  getGlobalIoService().post(bind(&DatagramFace<T, U>::keepFaceAliveUntilAllHandlersExecuted,
-                                 this, this->shared_from_this()));
-}
-
-template<class T, class U>
-inline bool
-DatagramFace<T, U>::hasBeenUsedRecently() const
-{
-  return m_hasBeenUsedRecently;
-}
-
-template<class T, class U>
-inline void
-DatagramFace<T, U>::resetRecentUsage()
-{
-  m_hasBeenUsedRecently = false;
-}
-
-} // namespace nfd
-
-#endif // NFD_DAEMON_FACE_DATAGRAM_FACE_HPP
diff --git a/daemon/face/datagram-transport.hpp b/daemon/face/datagram-transport.hpp
new file mode 100644
index 0000000..f9cf2fc
--- /dev/null
+++ b/daemon/face/datagram-transport.hpp
@@ -0,0 +1,239 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_FACE_DATAGRAM_TRANSPORT_HPP
+#define NFD_DAEMON_FACE_DATAGRAM_TRANSPORT_HPP
+
+#include "transport.hpp"
+#include "core/global-io.hpp"
+
+#include <array>
+
+namespace nfd {
+namespace face {
+
+struct Unicast {};
+struct Multicast {};
+
+/** \brief Implements Transport for datagram-based protocols.
+ *
+ *  \tparam Protocol a datagram-based protocol in Boost.Asio
+ */
+template<class Protocol, class Addressing = Unicast>
+class DatagramTransport : public Transport
+{
+public:
+  typedef Protocol protocol;
+
+  /** \brief Construct datagram transport.
+   *
+   *  \param socket Protocol-specific socket for the created transport
+   */
+  explicit
+  DatagramTransport(typename protocol::socket&& socket);
+
+  virtual void
+  doSend(Transport::Packet&& packet) DECL_OVERRIDE;
+
+  virtual void
+  doClose() DECL_OVERRIDE;
+
+  /** \brief Receive datagram, translate buffer into packet, deliver to parent class.
+   */
+  void
+  receiveDatagram(const uint8_t* buffer, size_t nBytesReceived,
+                  const boost::system::error_code& error);
+
+protected:
+  void
+  handleReceive(const boost::system::error_code& error,
+                size_t nBytesReceived);
+
+  void
+  handleSend(const boost::system::error_code& error,
+             size_t nBytesSent, const Block& payload);
+
+  void
+  processErrorCode(const boost::system::error_code& error);
+
+  bool
+  hasBeenUsedRecently() const;
+
+  void
+  resetRecentUsage();
+
+protected:
+  typename protocol::socket m_socket;
+
+  NFD_LOG_INCLASS_DECLARE();
+
+private:
+  std::array<uint8_t, ndn::MAX_NDN_PACKET_SIZE> m_receiveBuffer;
+  bool m_hasBeenUsedRecently;
+};
+
+
+template<class T, class U>
+inline
+DatagramTransport<T, U>::DatagramTransport(typename DatagramTransport::protocol::socket&& socket)
+  : m_socket(std::move(socket))
+{
+  m_socket.async_receive(boost::asio::buffer(m_receiveBuffer),
+                         bind(&DatagramTransport<T, U>::handleReceive, this,
+                              boost::asio::placeholders::error,
+                              boost::asio::placeholders::bytes_transferred));
+}
+
+template<class T, class U>
+inline void
+DatagramTransport<T, U>::doSend(Transport::Packet&& packet)
+{
+  NFD_LOG_FACE_TRACE(__func__);
+
+  m_socket.async_send(boost::asio::buffer(packet.packet),
+                      bind(&DatagramTransport<T, U>::handleSend, this,
+                           boost::asio::placeholders::error,
+                           boost::asio::placeholders::bytes_transferred,
+                           packet.packet));
+}
+
+template<class T, class U>
+inline void
+DatagramTransport<T, U>::doClose()
+{
+  NFD_LOG_FACE_TRACE(__func__);
+
+  if (m_socket.is_open()) {
+    // Cancel all outstanding operations and close the socket.
+    // Use the non-throwing variants and ignore errors, if any.
+    boost::system::error_code error;
+    m_socket.cancel(error);
+    m_socket.close(error);
+  }
+
+  // Ensure that the Transport stays alive at least until
+  // all pending handlers are dispatched
+  getGlobalIoService().post([this] {
+    this->setState(TransportState::CLOSED);
+  });
+}
+
+template<class T, class U>
+inline void
+DatagramTransport<T, U>::receiveDatagram(const uint8_t* buffer, size_t nBytesReceived,
+                                         const boost::system::error_code& error)
+{
+  if (error)
+    return processErrorCode(error);
+
+  NFD_LOG_FACE_TRACE("Received: " << nBytesReceived << " bytes");
+
+  bool isOk = false;
+  Block element;
+  std::tie(isOk, element) = Block::fromBuffer(buffer, nBytesReceived);
+  if (!isOk) {
+    NFD_LOG_FACE_WARN("Failed to parse incoming packet");
+    // This packet won't extend the face lifetime
+    return;
+  }
+  if (element.size() != nBytesReceived) {
+    NFD_LOG_FACE_WARN("Received datagram size and decoded element size don't match");
+    // This packet won't extend the face lifetime
+    return;
+  }
+
+  m_hasBeenUsedRecently = true;
+  this->receive(Transport::Packet(std::move(element)));
+}
+
+template<class T, class U>
+inline void
+DatagramTransport<T, U>::handleReceive(const boost::system::error_code& error,
+                                       size_t nBytesReceived)
+{
+  receiveDatagram(m_receiveBuffer.data(), nBytesReceived, error);
+
+  if (m_socket.is_open())
+    m_socket.async_receive(boost::asio::buffer(m_receiveBuffer),
+                           bind(&DatagramTransport<T, U>::handleReceive, this,
+                                boost::asio::placeholders::error,
+                                boost::asio::placeholders::bytes_transferred));
+}
+
+template<class T, class U>
+inline void
+DatagramTransport<T, U>::handleSend(const boost::system::error_code& error,
+                                    size_t nBytesSent, const Block& payload)
+// 'payload' is unused; it's needed to retain the underlying Buffer
+{
+  if (error)
+    return processErrorCode(error);
+
+  NFD_LOG_FACE_TRACE("Successfully sent: " << nBytesSent << " bytes");
+}
+
+template<class T, class U>
+inline void
+DatagramTransport<T, U>::processErrorCode(const boost::system::error_code& error)
+{
+  NFD_LOG_FACE_TRACE(__func__);
+
+  if (getState() == TransportState::CLOSING ||
+      getState() == TransportState::FAILED ||
+      getState() == TransportState::CLOSED ||
+      error == boost::asio::error::operation_aborted)
+    // transport is shutting down, ignore any errors
+    return;
+
+  if (getPersistency() == ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERMANENT) {
+    NFD_LOG_FACE_DEBUG("Permanent face ignores error: " << error.message());
+    return;
+  }
+
+  if (error != boost::asio::error::eof)
+    NFD_LOG_FACE_WARN("Send or receive operation failed: " << error.message());
+
+  this->setState(TransportState::FAILED);
+  doClose();
+}
+
+template<class T, class U>
+inline bool
+DatagramTransport<T, U>::hasBeenUsedRecently() const
+{
+  return m_hasBeenUsedRecently;
+}
+
+template<class T, class U>
+inline void
+DatagramTransport<T, U>::resetRecentUsage()
+{
+  m_hasBeenUsedRecently = false;
+}
+
+} // namespace face
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_DATAGRAM_TRANSPORT_HPP
diff --git a/daemon/face/multicast-udp-face.cpp b/daemon/face/multicast-udp-face.cpp
deleted file mode 100644
index 8ac07af..0000000
--- a/daemon/face/multicast-udp-face.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2015,  Regents of the University of California,
- *                           Arizona Board of Regents,
- *                           Colorado State University,
- *                           University Pierre & Marie Curie, Sorbonne University,
- *                           Washington University in St. Louis,
- *                           Beijing Institute of Technology,
- *                           The University of Memphis.
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon).
- * See AUTHORS.md for complete list of NFD authors and contributors.
- *
- * NFD is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "multicast-udp-face.hpp"
-
-namespace nfd {
-
-NFD_LOG_INCLASS_2TEMPLATE_SPECIALIZATION_DEFINE(DatagramFace,
-                                                MulticastUdpFace::protocol, Multicast,
-                                                "MulticastUdpFace");
-
-MulticastUdpFace::MulticastUdpFace(const protocol::endpoint& multicastGroup,
-                                   const FaceUri& localUri,
-                                   protocol::socket recvSocket, protocol::socket sendSocket)
-  : DatagramFace(FaceUri(multicastGroup), localUri, std::move(recvSocket))
-  , m_multicastGroup(multicastGroup)
-  , m_sendSocket(std::move(sendSocket))
-{
-}
-
-const MulticastUdpFace::protocol::endpoint&
-MulticastUdpFace::getMulticastGroup() const
-{
-  return m_multicastGroup;
-}
-
-void
-MulticastUdpFace::sendInterest(const Interest& interest)
-{
-  NFD_LOG_FACE_TRACE(__func__);
-  this->emitSignal(onSendInterest, interest);
-  sendBlock(interest.wireEncode());
-}
-
-void
-MulticastUdpFace::sendData(const Data& data)
-{
-  NFD_LOG_FACE_TRACE(__func__);
-  /// \todo After this face implements duplicate suppression, onSendData should
-  ///       be emitted only when data is actually sent out. See also #2555
-  this->emitSignal(onSendData, data);
-  sendBlock(data.wireEncode());
-}
-
-void
-MulticastUdpFace::sendBlock(const Block& block)
-{
-  m_sendSocket.async_send_to(boost::asio::buffer(block.wire(), block.size()), m_multicastGroup,
-                             bind(&MulticastUdpFace::handleSend, this,
-                                  boost::asio::placeholders::error,
-                                  boost::asio::placeholders::bytes_transferred,
-                                  block));
-}
-
-} // namespace nfd
diff --git a/daemon/face/multicast-udp-face.hpp b/daemon/face/multicast-udp-face.hpp
deleted file mode 100644
index 7aab4a7..0000000
--- a/daemon/face/multicast-udp-face.hpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon).
- * See AUTHORS.md for complete list of NFD authors and contributors.
- *
- * NFD is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef NFD_DAEMON_FACE_MULTICAST_UDP_FACE_HPP
-#define NFD_DAEMON_FACE_MULTICAST_UDP_FACE_HPP
-
-#include "datagram-face.hpp"
-
-namespace nfd {
-
-/**
- * \brief Implementation of Face abstraction that uses
- *        multicast UDP as underlying transport mechanism
- */
-class MulticastUdpFace : public DatagramFace<boost::asio::ip::udp, Multicast>
-{
-public:
-  /**
-   * \brief Creates a UDP-based face for multicast communication
-   */
-  MulticastUdpFace(const protocol::endpoint& multicastGroup, const FaceUri& localUri,
-                   protocol::socket recvSocket, protocol::socket sendSocket);
-
-  const protocol::endpoint&
-  getMulticastGroup() const;
-
-  // from Face
-  void
-  sendInterest(const Interest& interest) DECL_OVERRIDE;
-
-  void
-  sendData(const Data& data) DECL_OVERRIDE;
-
-private:
-  void
-  sendBlock(const Block& block);
-
-private:
-  protocol::endpoint m_multicastGroup;
-  protocol::socket m_sendSocket;
-};
-
-} // namespace nfd
-
-#endif // NFD_DAEMON_FACE_MULTICAST_UDP_FACE_HPP
diff --git a/daemon/face/multicast-udp-transport.cpp b/daemon/face/multicast-udp-transport.cpp
new file mode 100644
index 0000000..7befa78
--- /dev/null
+++ b/daemon/face/multicast-udp-transport.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "multicast-udp-transport.hpp"
+
+namespace nfd {
+namespace face {
+
+NFD_LOG_INCLASS_2TEMPLATE_SPECIALIZATION_DEFINE(DatagramTransport, MulticastUdpTransport::protocol,
+                                                Multicast, "MulticastUdpTransport");
+
+MulticastUdpTransport::MulticastUdpTransport(const protocol::endpoint& localEndpoint,
+                                             const protocol::endpoint& multicastGroup,
+                                             protocol::socket&& recvSocket,
+                                             protocol::socket&& sendSocket)
+  : DatagramTransport(std::move(recvSocket))
+  , m_multicastGroup(multicastGroup)
+  , m_sendSocket(std::move(sendSocket))
+{
+  this->setLocalUri(FaceUri(localEndpoint));
+  this->setRemoteUri(FaceUri(multicastGroup));
+  this->setPersistency(ndn::nfd::FACE_PERSISTENCY_PERMANENT);
+  this->setLinkType(ndn::nfd::LINK_TYPE_MULTI_ACCESS);
+
+  NFD_LOG_FACE_INFO("Creating transport");
+}
+
+void
+MulticastUdpTransport::doSend(Transport::Packet&& packet)
+{
+  NFD_LOG_FACE_TRACE(__func__);
+
+  m_sendSocket.async_send_to(boost::asio::buffer(packet.packet), m_multicastGroup,
+                             bind(&MulticastUdpTransport::handleSend, this,
+                                  boost::asio::placeholders::error,
+                                  boost::asio::placeholders::bytes_transferred,
+                                  packet.packet));
+}
+
+void
+MulticastUdpTransport::doClose()
+{
+  if (m_sendSocket.is_open()) {
+    NFD_LOG_FACE_TRACE("Closing sending socket");
+
+    // Cancel all outstanding operations and close the socket.
+    // Use the non-throwing variants and ignore errors, if any.
+    boost::system::error_code error;
+    m_sendSocket.cancel(error);
+    m_sendSocket.close(error);
+  }
+
+  DatagramTransport::doClose();
+}
+
+} // namespace face
+} // namespace nfd
diff --git a/daemon/face/multicast-udp-transport.hpp b/daemon/face/multicast-udp-transport.hpp
new file mode 100644
index 0000000..7966b5b
--- /dev/null
+++ b/daemon/face/multicast-udp-transport.hpp
@@ -0,0 +1,64 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_DAEMON_FACE_MULTICAST_UDP_TRANSPORT_HPP
+#define NFD_DAEMON_FACE_MULTICAST_UDP_TRANSPORT_HPP
+
+#include "datagram-transport.hpp"
+
+namespace nfd {
+namespace face {
+
+/**
+ * \brief A Transport that communicates on a UDP multicast group
+ */
+class MulticastUdpTransport : public DatagramTransport<boost::asio::ip::udp, Multicast>
+{
+public:
+  /**
+   * \brief Creates a UDP-based transport for multicast communication
+   * \param recvSocket socket used to receive packets
+   * \param sendSocket socket used to send to the multicast address
+   */
+  MulticastUdpTransport(const protocol::endpoint& localEndpoint,
+                        const protocol::endpoint& multicastGroup,
+                        protocol::socket&& recvSocket, protocol::socket&& sendSocket);
+
+private:
+  virtual void
+  doSend(Transport::Packet&& packet) DECL_OVERRIDE;
+
+  virtual void
+  doClose() DECL_OVERRIDE;
+
+private:
+  protocol::endpoint m_multicastGroup;
+  protocol::socket m_sendSocket;
+};
+
+} // namespace face
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_MULTICAST_UDP_TRANSPORT_HPP
diff --git a/daemon/face/udp-channel.cpp b/daemon/face/udp-channel.cpp
index c9920c4..a1ff92c 100644
--- a/daemon/face/udp-channel.cpp
+++ b/daemon/face/udp-channel.cpp
@@ -24,7 +24,8 @@
  */
 
 #include "udp-channel.hpp"
-#include "udp-face.hpp"
+#include "generic-link-service.hpp"
+#include "unicast-udp-transport.hpp"
 #include "core/global-io.hpp"
 
 namespace nfd {
@@ -71,7 +72,7 @@
                     const FaceCreatedCallback& onFaceCreated,
                     const ConnectFailedCallback& onConnectFailed)
 {
-  shared_ptr<UdpFace> face;
+  shared_ptr<face::LpFaceWrapper> face;
   try {
     face = createFace(remoteEndpoint, persistency).second;
   }
@@ -93,7 +94,7 @@
   return m_channelFaces.size();
 }
 
-std::pair<bool, shared_ptr<UdpFace>>
+std::pair<bool, shared_ptr<face::LpFaceWrapper>>
 UdpChannel::createFace(const udp::Endpoint& remoteEndpoint, ndn::nfd::FacePersistency persistency)
 {
   auto it = m_channelFaces.find(remoteEndpoint);
@@ -116,15 +117,18 @@
   socket.bind(m_localEndpoint);
   socket.connect(remoteEndpoint);
 
-  auto face = make_shared<UdpFace>(FaceUri(remoteEndpoint), FaceUri(m_localEndpoint),
-                                   std::move(socket), persistency, m_idleFaceTimeout);
+  auto linkService = make_unique<face::GenericLinkService>();
+  auto transport = make_unique<face::UnicastUdpTransport>(std::move(socket), persistency, m_idleFaceTimeout);
+  auto lpFace = make_unique<face::LpFace>(std::move(linkService), std::move(transport));
+  auto face = make_shared<face::LpFaceWrapper>(std::move(lpFace));
 
+  face->setPersistency(persistency);
   face->onFail.connectSingleShot([this, remoteEndpoint] (const std::string&) {
     NFD_LOG_TRACE("Erasing " << remoteEndpoint << " from channel face map");
     m_channelFaces.erase(remoteEndpoint);
   });
-  m_channelFaces[remoteEndpoint] = face;
 
+  m_channelFaces[remoteEndpoint] = face;
   return {true, face};
 }
 
@@ -147,7 +151,7 @@
   NFD_LOG_DEBUG("[" << m_localEndpoint << "] New peer " << m_remoteEndpoint);
 
   bool created;
-  shared_ptr<UdpFace> face;
+  shared_ptr<face::LpFaceWrapper> face;
   try {
     std::tie(created, face) = createFace(m_remoteEndpoint, ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
   }
@@ -163,7 +167,7 @@
     onFaceCreated(face);
 
   // dispatch the datagram to the face for processing
-  face->receiveDatagram(m_inputBuffer, nBytesReceived, error);
+  static_cast<face::UnicastUdpTransport*>(face->getLpFace()->getTransport())->receiveDatagram(m_inputBuffer, nBytesReceived, error);
 
   m_socket.async_receive_from(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE),
                               m_remoteEndpoint,
diff --git a/daemon/face/udp-channel.hpp b/daemon/face/udp-channel.hpp
index 3f92ada..0825ad4 100644
--- a/daemon/face/udp-channel.hpp
+++ b/daemon/face/udp-channel.hpp
@@ -1,12 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014,  Regents of the University of California,
- *                      Arizona Board of Regents,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University,
- *                      Washington University in St. Louis,
- *                      Beijing Institute of Technology,
- *                      The University of Memphis
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of NFD (Named Data Networking Forwarding Daemon).
  * See AUTHORS.md for complete list of NFD authors and contributors.
@@ -27,6 +27,7 @@
 #define NFD_DAEMON_FACE_UDP_CHANNEL_HPP
 
 #include "channel.hpp"
+#include "lp-face-wrapper.hpp"
 
 namespace nfd {
 
@@ -34,8 +35,6 @@
 typedef boost::asio::ip::udp::endpoint Endpoint;
 } // namespace udp
 
-class UdpFace;
-
 /**
  * \brief Class implementing UDP-based channel to create faces
  */
@@ -92,7 +91,7 @@
   isListening() const;
 
 private:
-  std::pair<bool, shared_ptr<UdpFace>>
+  std::pair<bool, shared_ptr<face::LpFaceWrapper>>
   createFace(const udp::Endpoint& remoteEndpoint, ndn::nfd::FacePersistency persistency);
 
   /**
@@ -106,7 +105,7 @@
                 const ConnectFailedCallback& onReceiveFailed);
 
 private:
-  std::map<udp::Endpoint, shared_ptr<UdpFace>> m_channelFaces;
+  std::map<udp::Endpoint, shared_ptr<face::LpFaceWrapper>> m_channelFaces;
 
   udp::Endpoint m_localEndpoint;
 
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index 8d24bbe..74636d8 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -24,6 +24,9 @@
  */
 
 #include "udp-factory.hpp"
+#include "generic-link-service.hpp"
+#include "lp-face-wrapper.hpp"
+#include "multicast-udp-transport.hpp"
 #include "core/global-io.hpp"
 #include "core/network-interface.hpp"
 
@@ -117,11 +120,12 @@
   if (static_cast<bool>(channel))
     return channel;
 
-  //checking if the endpoint is already in use for multicast face
-  shared_ptr<MulticastUdpFace> multicast = findMulticastFace(endpoint);
-  if (static_cast<bool>(multicast))
+  // check if the endpoint is already used by a multicast face
+  auto face = findMulticastFace(endpoint);
+  if (face) {
     BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP unicast channel, local "
                                 "endpoint is already allocated for a UDP multicast face"));
+  }
 
   if (endpoint.address().is_multicast()) {
     BOOST_THROW_EXCEPTION(Error("This method is only for unicast channel. The provided "
@@ -146,15 +150,15 @@
   return createChannel(endpoint, timeout);
 }
 
-shared_ptr<MulticastUdpFace>
+shared_ptr<face::LpFaceWrapper>
 UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint,
                                 const udp::Endpoint& multicastEndpoint,
                                 const std::string& networkInterfaceName/* = ""*/)
 {
   // checking if the local and multicast endpoints are already in use for a multicast face
-  shared_ptr<MulticastUdpFace> face = findMulticastFace(localEndpoint);
-  if (static_cast<bool>(face)) {
-    if (face->getMulticastGroup() == multicastEndpoint)
+  auto face = findMulticastFace(localEndpoint);
+  if (face) {
+    if (face->getRemoteUri() == FaceUri(multicastEndpoint))
       return face;
     else
       BOOST_THROW_EXCEPTION(Error("Cannot create the requested UDP multicast face, local "
@@ -225,8 +229,12 @@
   }
 #endif
 
-  face = make_shared<MulticastUdpFace>(multicastEndpoint, FaceUri(localEndpoint),
-                                       std::move(receiveSocket), std::move(sendSocket));
+  auto linkService = make_unique<face::GenericLinkService>();
+  auto transport = make_unique<face::MulticastUdpTransport>(localEndpoint, multicastEndpoint,
+                                                            std::move(receiveSocket),
+                                                            std::move(sendSocket));
+  auto lpFace = make_unique<face::LpFace>(std::move(linkService), std::move(transport));
+  face = make_shared<face::LpFaceWrapper>(std::move(lpFace));
 
   face->onFail.connectSingleShot([this, localEndpoint] (const std::string& reason) {
     m_multicastFaces.erase(localEndpoint);
@@ -236,7 +244,7 @@
   return face;
 }
 
-shared_ptr<MulticastUdpFace>
+shared_ptr<face::LpFaceWrapper>
 UdpFactory::createMulticastFace(const std::string& localIp,
                                 const std::string& multicastIp,
                                 const std::string& multicastPort,
@@ -302,14 +310,14 @@
     return shared_ptr<UdpChannel>();
 }
 
-shared_ptr<MulticastUdpFace>
+shared_ptr<face::LpFaceWrapper>
 UdpFactory::findMulticastFace(const udp::Endpoint& localEndpoint)
 {
   MulticastFaceMap::iterator i = m_multicastFaces.find(localEndpoint);
   if (i != m_multicastFaces.end())
     return i->second;
   else
-    return shared_ptr<MulticastUdpFace>();
+    return nullptr;
 }
 
 std::list<shared_ptr<const Channel>>
diff --git a/daemon/face/udp-factory.hpp b/daemon/face/udp-factory.hpp
index 1a3714c..0465ca5 100644
--- a/daemon/face/udp-factory.hpp
+++ b/daemon/face/udp-factory.hpp
@@ -28,7 +28,6 @@
 
 #include "protocol-factory.hpp"
 #include "udp-channel.hpp"
-#include "multicast-udp-face.hpp"
 
 namespace nfd {
 
@@ -50,7 +49,7 @@
     }
   };
 
-  typedef std::map<udp::Endpoint, shared_ptr<MulticastUdpFace>> MulticastFaceMap;
+  typedef std::map<udp::Endpoint, shared_ptr<face::LpFaceWrapper>> MulticastFaceMap;
 
   explicit
   UdpFactory(const std::string& defaultPort = "6363");
@@ -131,12 +130,12 @@
    * \see http://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio/reference/ip__udp/endpoint.html
    *      for details on ways to create udp::Endpoint
    */
-  shared_ptr<MulticastUdpFace>
+  shared_ptr<face::LpFaceWrapper>
   createMulticastFace(const udp::Endpoint& localEndpoint,
                       const udp::Endpoint& multicastEndpoint,
                       const std::string& networkInterfaceName = "");
 
-  shared_ptr<MulticastUdpFace>
+  shared_ptr<face::LpFaceWrapper>
   createMulticastFace(const std::string& localIp,
                       const std::string& multicastIp,
                       const std::string& multicastPort,
@@ -186,11 +185,9 @@
    * \brief Look up multicast UdpFace using specified local endpoint
    *
    * \returns shared pointer to the existing multicast MulticastUdpFace object
-   *          or empty shared pointer when such face does not exist
-   *
-   * \throws never
+   *          or nullptr when such face does not exist
    */
-  shared_ptr<MulticastUdpFace>
+  shared_ptr<face::LpFaceWrapper>
   findMulticastFace(const udp::Endpoint& localEndpoint);
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
diff --git a/daemon/face/udp-face.cpp b/daemon/face/unicast-udp-transport.cpp
similarity index 63%
rename from daemon/face/udp-face.cpp
rename to daemon/face/unicast-udp-transport.cpp
index 6985784..eb8ab66 100644
--- a/daemon/face/udp-face.cpp
+++ b/daemon/face/unicast-udp-transport.cpp
@@ -23,8 +23,7 @@
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "udp-face.hpp"
-// #include "core/global-io.hpp" // for #1718 manual test below
+#include "unicast-udp-transport.hpp"
 
 #ifdef __linux__
 #include <cerrno>       // for errno
@@ -34,17 +33,24 @@
 #endif
 
 namespace nfd {
+namespace face {
 
-NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(DatagramFace, UdpFace::protocol, "UdpFace");
+NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(DatagramTransport, UnicastUdpTransport::protocol,
+                                               "UnicastUdpTransport");
 
-UdpFace::UdpFace(const FaceUri& remoteUri, const FaceUri& localUri,
-                 protocol::socket socket, ndn::nfd::FacePersistency persistency,
-                 const time::seconds& idleTimeout)
-  : DatagramFace(remoteUri, localUri, std::move(socket))
+UnicastUdpTransport::UnicastUdpTransport(protocol::socket&& socket,
+                                         ndn::nfd::FacePersistency persistency,
+                                         const time::seconds& idleTimeout)
+  : DatagramTransport(std::move(socket))
   , m_idleTimeout(idleTimeout)
   , m_lastIdleCheck(time::steady_clock::now())
 {
+  this->setLocalUri(FaceUri(m_socket.local_endpoint()));
+  this->setRemoteUri(FaceUri(m_socket.remote_endpoint()));
   this->setPersistency(persistency);
+  this->setLinkType(ndn::nfd::LINK_TYPE_POINT_TO_POINT);
+
+  NFD_LOG_FACE_INFO("Creating transport");
 
 #ifdef __linux__
   //
@@ -66,56 +72,31 @@
   }
 #endif
 
-  if (this->getPersistency() == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND && m_idleTimeout > time::seconds::zero()) {
-    m_closeIfIdleEvent = scheduler::schedule(m_idleTimeout, bind(&UdpFace::closeIfIdle, this));
+  if (getPersistency() == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND &&
+      m_idleTimeout > time::seconds::zero()) {
+    m_closeIfIdleEvent = scheduler::schedule(m_idleTimeout, bind(&UnicastUdpTransport::closeIfIdle, this));
   }
 }
 
-ndn::nfd::FaceStatus
-UdpFace::getFaceStatus() const
-{
-  auto status = DatagramFace::getFaceStatus();
-
-  if (this->getPersistency() == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
-    time::milliseconds left = m_idleTimeout;
-    left -= time::duration_cast<time::milliseconds>(time::steady_clock::now() - m_lastIdleCheck);
-
-    if (left < time::milliseconds::zero())
-      left = time::milliseconds::zero();
-
-    if (hasBeenUsedRecently())
-      left += m_idleTimeout;
-
-    status.setExpirationPeriod(left);
-  }
-
-  return status;
-}
-
 void
-UdpFace::closeIfIdle()
+UnicastUdpTransport::closeIfIdle()
 {
-  // Face can be switched from on-demand to non-on-demand mode
+  // transport can be switched from on-demand to non-on-demand mode
   // (non-on-demand -> on-demand transition is not allowed)
-  if (this->getPersistency() == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
+  if (getPersistency() == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
     if (!hasBeenUsedRecently()) {
-      NFD_LOG_FACE_INFO("Closing for inactivity");
-      close();
-
-      // #1718 manual test: uncomment, run NFD in valgrind, send in a UDP packet
-      //                    expect read-after-free error and crash
-      // getGlobalIoService().post([this] {
-      //   NFD_LOG_FACE_ERROR("Remaining references: " << this->shared_from_this().use_count());
-      // });
+      NFD_LOG_FACE_INFO("Closing due to inactivity");
+      this->close();
     }
     else {
       resetRecentUsage();
 
       m_lastIdleCheck = time::steady_clock::now();
-      m_closeIfIdleEvent = scheduler::schedule(m_idleTimeout, bind(&UdpFace::closeIfIdle, this));
+      m_closeIfIdleEvent = scheduler::schedule(m_idleTimeout, bind(&UnicastUdpTransport::closeIfIdle, this));
     }
   }
   // else do nothing and do not reschedule the event
 }
 
+} // namespace face
 } // namespace nfd
diff --git a/daemon/face/udp-face.hpp b/daemon/face/unicast-udp-transport.hpp
similarity index 70%
rename from daemon/face/udp-face.hpp
rename to daemon/face/unicast-udp-transport.hpp
index a7541a5..2622e4b 100644
--- a/daemon/face/udp-face.hpp
+++ b/daemon/face/unicast-udp-transport.hpp
@@ -23,27 +23,24 @@
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef NFD_DAEMON_FACE_UDP_FACE_HPP
-#define NFD_DAEMON_FACE_UDP_FACE_HPP
+#ifndef NFD_DAEMON_FACE_UDP_TRANSPORT_HPP
+#define NFD_DAEMON_FACE_UDP_TRANSPORT_HPP
 
-#include "datagram-face.hpp"
+#include "datagram-transport.hpp"
 #include "core/scheduler.hpp"
 
 namespace nfd {
+namespace face {
 
 /**
- * \brief Implementation of Face abstraction that uses
- *        unicast UDP as underlying transport mechanism
+ * \brief A Transport that communicates on a unicast UDP socket
  */
-class UdpFace : public DatagramFace<boost::asio::ip::udp>
+class UnicastUdpTransport : public DatagramTransport<boost::asio::ip::udp>
 {
 public:
-  UdpFace(const FaceUri& remoteUri, const FaceUri& localUri,
-          protocol::socket socket, ndn::nfd::FacePersistency persistency,
-          const time::seconds& idleTimeout);
-
-  ndn::nfd::FaceStatus
-  getFaceStatus() const DECL_OVERRIDE;
+  UnicastUdpTransport(protocol::socket&& socket,
+                      ndn::nfd::FacePersistency persistency,
+                      const time::seconds& idleTimeout);
 
 private:
   void
@@ -53,11 +50,9 @@
   const time::seconds m_idleTimeout;
   time::steady_clock::TimePoint m_lastIdleCheck;
   scheduler::ScopedEventId m_closeIfIdleEvent;
-
-  // friend because it needs to invoke protected Face::setOnDemand
-  friend class UdpChannel;
 };
 
+} // namespace face
 } // namespace nfd
 
-#endif // NFD_DAEMON_FACE_UDP_FACE_HPP
+#endif // NFD_DAEMON_FACE_UDP_TRANSPORT_HPP