face: minimal UDP permanent face

Change-Id: I44f652b95c911667107fd30829c7ce6e5acab706
Refs: #2989
diff --git a/daemon/face/datagram-face.hpp b/daemon/face/datagram-face.hpp
index 82d0347..a55fb8c 100644
--- a/daemon/face/datagram-face.hpp
+++ b/daemon/face/datagram-face.hpp
@@ -168,7 +168,11 @@
   if (error == boost::asio::error::operation_aborted) // when cancel() is called
     return;
 
-  // this should be unnecessary, but just in case
+  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;
diff --git a/daemon/face/udp-channel.cpp b/daemon/face/udp-channel.cpp
index 81cf401..c9920c4 100644
--- a/daemon/face/udp-channel.cpp
+++ b/daemon/face/udp-channel.cpp
@@ -67,12 +67,13 @@
 
 void
 UdpChannel::connect(const udp::Endpoint& remoteEndpoint,
+                    ndn::nfd::FacePersistency persistency,
                     const FaceCreatedCallback& onFaceCreated,
                     const ConnectFailedCallback& onConnectFailed)
 {
   shared_ptr<UdpFace> face;
   try {
-    face = createFace(remoteEndpoint, false).second;
+    face = createFace(remoteEndpoint, persistency).second;
   }
   catch (const boost::system::system_error& e) {
     NFD_LOG_WARN("[" << m_localEndpoint << "] Connect failed: " << e.what());
@@ -93,16 +94,20 @@
 }
 
 std::pair<bool, shared_ptr<UdpFace>>
-UdpChannel::createFace(const udp::Endpoint& remoteEndpoint, bool isOnDemand)
+UdpChannel::createFace(const udp::Endpoint& remoteEndpoint, ndn::nfd::FacePersistency persistency)
 {
   auto it = m_channelFaces.find(remoteEndpoint);
   if (it != m_channelFaces.end()) {
     // we already have a face for this endpoint, just reuse it
-    if (!isOnDemand) {
-      // only on-demand -> persistent transition is allowed
-      it->second->setPersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+    auto face = it->second;
+    // only on-demand -> persistent -> permanent transition is allowed
+    bool isTransitionAllowed = persistency != face->getPersistency() &&
+                               (face->getPersistency() == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND ||
+                                persistency == ndn::nfd::FACE_PERSISTENCY_PERMANENT);
+    if (isTransitionAllowed) {
+      face->setPersistency(persistency);
     }
-    return {false, it->second};
+    return {false, face};
   }
 
   // else, create a new face
@@ -112,7 +117,7 @@
   socket.connect(remoteEndpoint);
 
   auto face = make_shared<UdpFace>(FaceUri(remoteEndpoint), FaceUri(m_localEndpoint),
-                                   std::move(socket), isOnDemand, m_idleFaceTimeout);
+                                   std::move(socket), persistency, m_idleFaceTimeout);
 
   face->onFail.connectSingleShot([this, remoteEndpoint] (const std::string&) {
     NFD_LOG_TRACE("Erasing " << remoteEndpoint << " from channel face map");
@@ -144,7 +149,7 @@
   bool created;
   shared_ptr<UdpFace> face;
   try {
-    std::tie(created, face) = createFace(m_remoteEndpoint, true);
+    std::tie(created, face) = createFace(m_remoteEndpoint, ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
   }
   catch (const boost::system::system_error& e) {
     NFD_LOG_WARN("[" << m_localEndpoint << "] Failed to create face for peer "
diff --git a/daemon/face/udp-channel.hpp b/daemon/face/udp-channel.hpp
index 17410dc..3f92ada 100644
--- a/daemon/face/udp-channel.hpp
+++ b/daemon/face/udp-channel.hpp
@@ -78,6 +78,7 @@
    */
   void
   connect(const udp::Endpoint& remoteEndpoint,
+          ndn::nfd::FacePersistency persistency,
           const FaceCreatedCallback& onFaceCreated,
           const ConnectFailedCallback& onConnectFailed);
 
@@ -92,7 +93,7 @@
 
 private:
   std::pair<bool, shared_ptr<UdpFace>>
-  createFace(const udp::Endpoint& remoteEndpoint, bool isOnDemand);
+  createFace(const udp::Endpoint& remoteEndpoint, ndn::nfd::FacePersistency persistency);
 
   /**
    * \brief The channel has received a new packet from a remote
diff --git a/daemon/face/udp-face.cpp b/daemon/face/udp-face.cpp
index 20b57cf..6985784 100644
--- a/daemon/face/udp-face.cpp
+++ b/daemon/face/udp-face.cpp
@@ -38,13 +38,13 @@
 NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(DatagramFace, UdpFace::protocol, "UdpFace");
 
 UdpFace::UdpFace(const FaceUri& remoteUri, const FaceUri& localUri,
-                 protocol::socket socket, bool isOnDemand,
+                 protocol::socket socket, ndn::nfd::FacePersistency persistency,
                  const time::seconds& idleTimeout)
   : DatagramFace(remoteUri, localUri, std::move(socket))
   , m_idleTimeout(idleTimeout)
   , m_lastIdleCheck(time::steady_clock::now())
 {
-  this->setPersistency(isOnDemand ? ndn::nfd::FACE_PERSISTENCY_ON_DEMAND : ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+  this->setPersistency(persistency);
 
 #ifdef __linux__
   //
diff --git a/daemon/face/udp-face.hpp b/daemon/face/udp-face.hpp
index 333bae2..a7541a5 100644
--- a/daemon/face/udp-face.hpp
+++ b/daemon/face/udp-face.hpp
@@ -39,7 +39,7 @@
 {
 public:
   UdpFace(const FaceUri& remoteUri, const FaceUri& localUri,
-          protocol::socket socket, bool isOnDemand,
+          protocol::socket socket, ndn::nfd::FacePersistency persistency,
           const time::seconds& idleTimeout);
 
   ndn::nfd::FaceStatus
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index 07b0367..8d24bbe 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -255,8 +255,8 @@
                        const FaceCreatedCallback& onCreated,
                        const FaceConnectFailedCallback& onConnectFailed)
 {
-  if (persistency != ndn::nfd::FACE_PERSISTENCY_PERSISTENT) {
-    BOOST_THROW_EXCEPTION(Error("UdpFactory only supports persistent face"));
+  if (persistency == ndn::nfd::FacePersistency::FACE_PERSISTENCY_ON_DEMAND) {
+    BOOST_THROW_EXCEPTION(Error("UdpFactory::createFace does not support creating on-demand face"));
   }
 
   BOOST_ASSERT(uri.isCanonical());
@@ -283,7 +283,7 @@
     if ((channel->first.address().is_v4() && endpoint.address().is_v4()) ||
         (channel->first.address().is_v6() && endpoint.address().is_v6()))
     {
-      channel->second->connect(endpoint, onCreated, onConnectFailed);
+      channel->second->connect(endpoint, persistency, onCreated, onConnectFailed);
       return;
     }
   }