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;
     }
   }
diff --git a/tests/daemon/face/udp.t.cpp b/tests/daemon/face/udp.t.cpp
index ff0c710..c00f54f 100644
--- a/tests/daemon/face/udp.t.cpp
+++ b/tests/daemon/face/udp.t.cpp
@@ -228,22 +228,25 @@
                      ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
                      bind([]{}),
                      bind(&FaceCreateFixture::failIfError, this, _1));
+  //test the upgrade
+  factory.createFace(FaceUri("udp4://127.0.0.1:20070"),
+                     ndn::nfd::FACE_PERSISTENCY_PERMANENT,
+                     bind([]{}),
+                     bind(&FaceCreateFixture::failIfError, this, _1));
+
+  factory.createFace(FaceUri("udp4://127.0.0.1:20072"),
+                     ndn::nfd::FACE_PERSISTENCY_PERMANENT,
+                     bind([]{}),
+                     bind(&FaceCreateFixture::failIfError, this, _1));
 }
 
-BOOST_FIXTURE_TEST_CASE(UnsupportedFaceCreate, FaceCreateFixture)
+BOOST_AUTO_TEST_CASE(UnsupportedFaceCreate)
 {
   UdpFactory factory;
 
   factory.createChannel("127.0.0.1", "20070");
-  factory.createChannel("127.0.0.1", "20071");
 
   BOOST_CHECK_THROW(factory.createFace(FaceUri("udp4://127.0.0.1:20070"),
-                                       ndn::nfd::FACE_PERSISTENCY_PERMANENT,
-                                       bind([]{}),
-                                       bind([]{})),
-                    ProtocolFactory::Error);
-
-  BOOST_CHECK_THROW(factory.createFace(FaceUri("udp4://127.0.0.1:20071"),
                                        ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
                                        bind([]{}),
                                        bind([]{})),
@@ -480,6 +483,7 @@
   boost::asio::ip::address ipAddress2 = boost::asio::ip::address::from_string(A::getLocalIp());
   udp::Endpoint endpoint2(ipAddress2, boost::lexical_cast<uint16_t>(A::getPort1()));
   channel2->connect(endpoint2,
+                    ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
                     [&] (shared_ptr<Face> newFace) {
                       face2 = newFace;
                       limitedIo.afterOp();
@@ -497,6 +501,7 @@
   boost::asio::ip::address ipAddress3 = boost::asio::ip::address::from_string(A::getLocalIp());
   udp::Endpoint endpoint3(ipAddress3, boost::lexical_cast<uint16_t>(A::getPort1()));
   channel3->connect(endpoint3,
+                    ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
                     [&] (shared_ptr<Face> newFace) {
                       face3 = newFace;
                       limitedIo.afterOp();
@@ -579,6 +584,7 @@
   boost::asio::ip::address ipAddress = boost::asio::ip::address::from_string(A::getLocalIp());
   udp::Endpoint endpoint(ipAddress, boost::lexical_cast<uint16_t>(A::getPort1()));
   channel2->connect(endpoint,
+                    ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
                     [&] (shared_ptr<Face> newFace) {
                       face2 = newFace;
                       history2.reset(new FaceHistory(*face2, limitedIo));