tools: add reliability option to nfdc face create

add LpReliability flag to FaceManager face status output

refs #4004

Change-Id: Ibcdfe7ff0fc9790cbcc4f2aa5e57e27b8a76023c
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index c3cd1bd..936b71e 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -424,6 +424,8 @@
   if (linkService != nullptr) {
     auto linkServiceOptions = linkService->getOptions();
     traits.setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED, linkServiceOptions.allowLocalFields);
+    traits.setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED,
+                      linkServiceOptions.reliabilityOptions.isEnabled);
   }
 }
 
diff --git a/docs/manpages/nfdc-face.rst b/docs/manpages/nfdc-face.rst
index cfec8e2..2e21b1e 100644
--- a/docs/manpages/nfdc-face.rst
+++ b/docs/manpages/nfdc-face.rst
@@ -5,7 +5,7 @@
 --------
 | nfdc face [list [[remote] <FACEURI>] [local <FACEURI>] [scheme <SCHEME>]]
 | nfdc face show [id] <FACEID>
-| nfdc face create [remote] <FACEURI> [[persistency] <PERSISTENCY>] [local <FACEURI>]
+| nfdc face create [remote] <FACEURI> [[persistency] <PERSISTENCY>] [local <FACEURI>] [reliability on|off]
 | nfdc face destroy [face] <FACEID|FACEURI>
 | nfdc channel [list]
 
@@ -24,6 +24,9 @@
 
 The **nfdc face create** command creates a UDP unicast, TCP, or Ethernet unicast face.
 Local FaceUri is required for creating Ethernet unicast faces; otherwise it must be omitted.
+The NDNLPv2 unicast reliability feature may be explicitly enabled by specifying **reliability on**
+or explicitly disabled by specifying **reliability off**.
+If enabled, this feature must also be enabled on the other endpoint to function properly.
 
 The **nfdc face destroy** command destroys an existing face.
 
diff --git a/tests/tools/nfdc/face-module.t.cpp b/tests/tools/nfdc/face-module.t.cpp
index ad7aa0e..33e022c 100644
--- a/tests/tools/nfdc/face-module.t.cpp
+++ b/tests/tools/nfdc/face-module.t.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -46,7 +46,7 @@
     " flags={non-local permanent multi-access}\n"
   "faceid=745 remote=fd://75 local=unix:///var/run/nfd.sock"
     " counters={in={18998i 26701d 147n 4672308B} out={34779i 17028d 1176n 8957187B}}"
-    " flags={local on-demand point-to-point local-fields}\n";
+    " flags={local on-demand point-to-point local-fields lp-reliability}\n";
 
 BOOST_AUTO_TEST_CASE(NormalNonQuery)
 {
@@ -74,6 +74,7 @@
             .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_ON_DEMAND)
             .setLinkType(ndn::nfd::LINK_TYPE_POINT_TO_POINT)
             .setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED, true)
+            .setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, true)
             .setNInInterests(18998)
             .setNInData(26701)
             .setNInNacks(147)
@@ -227,7 +228,7 @@
 {
 protected:
   void
-  respond409(const Interest& interest, FacePersistency persistency)
+  respond409(const Interest& interest, FacePersistency persistency, bool enableLpReliability = false)
   {
     MOCK_NFD_MGMT_REQUIRE_COMMAND_IS("/localhost/nfd/faces/create");
     ControlParameters body;
@@ -236,6 +237,9 @@
         .setLocalUri("udp4://68.62.26.57:24087")
         .setFacePersistency(persistency)
         .setFlags(0);
+    if (enableLpReliability) {
+      body.setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, true, false);
+    }
     this->failCommand(interest, 409, "conflict-409", body);
   }
 };
@@ -264,11 +268,12 @@
   this->execute("face create udp://159.242.33.78");
   BOOST_CHECK_EQUAL(exitCode, 0);
   BOOST_CHECK(out.is_equal("face-created id=2130 local=udp4://179.63.153.45:28835 "
-                           "remote=udp4://159.242.33.78:6363 persistency=persistent\n"));
+                           "remote=udp4://159.242.33.78:6363 persistency=persistent "
+                           "reliability=off\n"));
   BOOST_CHECK(err.is_empty());
 }
 
-BOOST_AUTO_TEST_CASE(CreatingWithLocalUri)
+BOOST_AUTO_TEST_CASE(CreatingWithParams)
 {
   this->processInterest = [this] (const Interest& interest) {
     ControlParameters req = MOCK_NFD_MGMT_REQUIRE_COMMAND_IS("/localhost/nfd/faces/create");
@@ -278,20 +283,23 @@
     BOOST_CHECK_EQUAL(req.getLocalUri(), "udp4://98.68.23.71:6363");
     BOOST_REQUIRE(req.hasFacePersistency());
     BOOST_CHECK_EQUAL(req.getFacePersistency(), FacePersistency::FACE_PERSISTENCY_PERMANENT);
+    BOOST_CHECK(req.hasFlags());
+    BOOST_CHECK(req.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED));
 
     ControlParameters resp;
     resp.setFaceId(301)
         .setUri("udp4://22.91.89.51:19903")
         .setLocalUri("udp4://98.68.23.71:6363")
         .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERMANENT)
-        .setFlags(0);
+        .setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, true, false);
     this->succeedCommand(interest, resp);
   };
 
-  this->execute("face create udp://22.91.89.51:19903 permanent local udp://98.68.23.71");
+  this->execute("face create udp://22.91.89.51:19903 permanent local udp://98.68.23.71 reliability on");
   BOOST_CHECK_EQUAL(exitCode, 0);
   BOOST_CHECK(out.is_equal("face-created id=301 local=udp4://98.68.23.71:6363 "
-                           "remote=udp4://22.91.89.51:19903 persistency=permanent\n"));
+                           "remote=udp4://22.91.89.51:19903 persistency=permanent "
+                           "reliability=on\n"));
   BOOST_CHECK(err.is_empty());
 }
 
@@ -323,7 +331,8 @@
   BOOST_CHECK(hasUpdateCommand);
   BOOST_CHECK_EQUAL(exitCode, 0);
   BOOST_CHECK(out.is_equal("face-updated id=1172 local=udp4://68.62.26.57:24087 "
-                           "remote=udp4://100.77.30.65:6363 persistency=persistent\n"));
+                           "remote=udp4://100.77.30.65:6363 persistency=persistent "
+                           "reliability=off\n"));
   BOOST_CHECK(err.is_empty());
 }
 
@@ -337,7 +346,8 @@
   this->execute("face create udp://100.77.30.65");
   BOOST_CHECK_EQUAL(exitCode, 0);
   BOOST_CHECK(out.is_equal("face-exists id=1172 local=udp4://68.62.26.57:24087 "
-                           "remote=udp4://100.77.30.65:6363 persistency=permanent\n"));
+                           "remote=udp4://100.77.30.65:6363 persistency=permanent "
+                           "reliability=off\n"));
   BOOST_CHECK(err.is_empty());
 }
 
@@ -351,7 +361,70 @@
   this->execute("face create udp://100.77.30.65");
   BOOST_CHECK_EQUAL(exitCode, 0);
   BOOST_CHECK(out.is_equal("face-exists id=1172 local=udp4://68.62.26.57:24087 "
-                           "remote=udp4://100.77.30.65:6363 persistency=persistent\n"));
+                           "remote=udp4://100.77.30.65:6363 persistency=persistent "
+                           "reliability=off\n"));
+  BOOST_CHECK(err.is_empty());
+}
+
+BOOST_AUTO_TEST_CASE(EnablingReliability)
+{
+  bool hasUpdateCommand = false;
+  this->processInterest = [this, &hasUpdateCommand] (const Interest& interest) {
+    if (parseCommand(interest, "/localhost/nfd/faces/create")) {
+      this->respond409(interest, FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+      return;
+    }
+
+    ControlParameters req = MOCK_NFD_MGMT_REQUIRE_COMMAND_IS("/localhost/nfd/faces/update");
+    hasUpdateCommand = true;
+    BOOST_REQUIRE(req.hasFaceId());
+    BOOST_CHECK_EQUAL(req.getFaceId(), 1172);
+    BOOST_CHECK(req.hasFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED));
+    BOOST_CHECK(req.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED));
+
+    ControlParameters resp;
+    resp.setFaceId(1172)
+        .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERSISTENT)
+        .setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, true, false);
+    this->succeedCommand(interest, resp);
+  };
+
+  this->execute("face create udp://100.77.30.65 reliability on");
+  BOOST_CHECK_EQUAL(exitCode, 0);
+  BOOST_CHECK(out.is_equal("face-updated id=1172 local=udp4://68.62.26.57:24087 "
+                           "remote=udp4://100.77.30.65:6363 persistency=persistent "
+                           "reliability=on\n"));
+  BOOST_CHECK(err.is_empty());
+}
+
+BOOST_AUTO_TEST_CASE(DisablingReliability)
+{
+  bool hasUpdateCommand = false;
+  this->processInterest = [this, &hasUpdateCommand] (const Interest& interest) {
+    if (parseCommand(interest, "/localhost/nfd/faces/create")) {
+      this->respond409(interest, FacePersistency::FACE_PERSISTENCY_PERSISTENT, true);
+      return;
+    }
+
+    ControlParameters req = MOCK_NFD_MGMT_REQUIRE_COMMAND_IS("/localhost/nfd/faces/update");
+    hasUpdateCommand = true;
+    BOOST_REQUIRE(req.hasFaceId());
+    BOOST_CHECK_EQUAL(req.getFaceId(), 1172);
+    BOOST_CHECK(req.hasFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED));
+    BOOST_CHECK(!req.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED));
+
+    ControlParameters resp;
+    resp.setFaceId(1172)
+        .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERSISTENT)
+        .setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, false, false);
+    this->succeedCommand(interest, resp);
+  };
+
+  this->execute("face create udp://100.77.30.65 reliability off");
+  BOOST_CHECK_EQUAL(exitCode, 0);
+  BOOST_CHECK(out.is_equal("face-updated id=1172 local=udp4://68.62.26.57:24087 "
+                           "remote=udp4://100.77.30.65:6363 persistency=persistent "
+                           "reliability=off\n"));
   BOOST_CHECK(err.is_empty());
 }
 
@@ -438,7 +511,8 @@
   this->execute("face destroy 10156");
   BOOST_CHECK_EQUAL(exitCode, 0);
   BOOST_CHECK(out.is_equal("face-destroyed id=10156 local=tcp4://151.26.163.27:22967 "
-                           "remote=tcp4://198.57.27.40:6363 persistency=persistent\n"));
+                           "remote=tcp4://198.57.27.40:6363 persistency=persistent "
+                           "reliability=off\n"));
   BOOST_CHECK(err.is_empty());
 }
 
@@ -461,7 +535,8 @@
   this->execute("face destroy tcp://32.121.182.82");
   BOOST_CHECK_EQUAL(exitCode, 0);
   BOOST_CHECK(out.is_equal("face-destroyed id=2249 local=tcp4://30.99.87.98:31414 "
-                           "remote=tcp4://32.121.182.82:6363 persistency=persistent\n"));
+                           "remote=tcp4://32.121.182.82:6363 persistency=persistent "
+                           "reliability=off\n"));
   BOOST_CHECK(err.is_empty());
 }
 
@@ -566,6 +641,7 @@
       <linkType>point-to-point</linkType>
       <flags>
         <localFieldsEnabled/>
+        <lpReliabilityEnabled/>
       </flags>
       <packetCounters>
         <incomingPackets>
@@ -594,7 +670,7 @@
     " flags={non-local permanent multi-access}\n"
   "  faceid=745 remote=fd://75 local=unix:///var/run/nfd.sock"
     " counters={in={18998i 26701d 147n 4672308B} out={34779i 17028d 1176n 8957187B}}"
-    " flags={local on-demand point-to-point local-fields}\n";
+    " flags={local on-demand point-to-point local-fields lp-reliability}\n";
 
 BOOST_FIXTURE_TEST_CASE(Status, StatusFixture<FaceModule>)
 {
@@ -622,6 +698,7 @@
           .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_ON_DEMAND)
           .setLinkType(ndn::nfd::LINK_TYPE_POINT_TO_POINT)
           .setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED, true)
+          .setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, true)
           .setNInInterests(18998)
           .setNInData(26701)
           .setNInNacks(147)
diff --git a/tools/nfdc/command-arguments.hpp b/tools/nfdc/command-arguments.hpp
index 49e7419..13aef61 100644
--- a/tools/nfdc/command-arguments.hpp
+++ b/tools/nfdc/command-arguments.hpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -28,8 +28,11 @@
 
 #include "core/common.hpp"
 #include "status-report.hpp"
+
 #include <ndn-cxx/encoding/nfd-constants.hpp>
+
 #include <boost/any.hpp>
+#include <boost/logic/tribool.hpp>
 
 namespace nfd {
 namespace tools {
@@ -65,6 +68,19 @@
     }
     return boost::any_cast<T>(i->second);
   }
+
+  /** \brief get an optional boolean argument as tribool
+   *  \return the argument value, or boost::logic::indeterminate if the argument is omitted on command line
+   */
+  boost::logic::tribool
+  getTribool(const std::string& key) const
+  {
+    auto value = getOptional<bool>(key);
+    if (value) {
+      return *value;
+    }
+    return boost::logic::indeterminate;
+  }
 };
 
 } // namespace nfdc
diff --git a/tools/nfdc/command-definition.cpp b/tools/nfdc/command-definition.cpp
index a2ceb33..27b30f2 100644
--- a/tools/nfdc/command-definition.cpp
+++ b/tools/nfdc/command-definition.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -40,6 +40,8 @@
       return os << "none";
     case ArgValueType::ANY:
       return os << "any";
+    case ArgValueType::BOOLEAN:
+      return os << "boolean";
     case ArgValueType::UNSIGNED:
       return os << "non-negative integer";
     case ArgValueType::STRING:
@@ -68,6 +70,8 @@
       return "";
     case ArgValueType::ANY:
       return "args";
+    case ArgValueType::BOOLEAN:
+      return "bool";
     case ArgValueType::UNSIGNED:
       return "uint";
     case ArgValueType::STRING:
@@ -138,7 +142,7 @@
       const Arg& arg = namedArg->second;
       if (arg.valueType == ArgValueType::NONE) {
         ca[arg.name] = true;
-        NDN_LOG_TRACE(token << " is a boolean argument");
+        NDN_LOG_TRACE(token << " is a no-param argument");
       }
       else if (i + 1 >= tokens.size()) {
         BOOST_THROW_EXCEPTION(Error(arg.name + ": " + arg.metavar + " is missing"));
@@ -212,6 +216,18 @@
   return ca;
 }
 
+static bool
+parseBoolean(const std::string& s)
+{
+  if (s == "on" || s == "true" || s == "enabled" || s == "yes" || s == "1") {
+    return true;
+  }
+  if (s == "off" || s == "false" || s == "disabled" || s == "no" || s == "0") {
+    return false;
+  }
+  BOOST_THROW_EXCEPTION(std::invalid_argument("unrecognized boolean value '" + s + "'"));
+}
+
 static FacePersistency
 parseFacePersistency(const std::string& s)
 {
@@ -221,7 +237,7 @@
   if (s == "permanent") {
     return FacePersistency::FACE_PERSISTENCY_PERMANENT;
   }
-  BOOST_THROW_EXCEPTION(std::invalid_argument("unrecognized FacePersistency"));
+  BOOST_THROW_EXCEPTION(std::invalid_argument("unrecognized FacePersistency '" + s + "'"));
 }
 
 boost::any
@@ -233,11 +249,15 @@
       BOOST_ASSERT(false);
       return boost::any();
 
+    case ArgValueType::BOOLEAN: {
+      return parseBoolean(token);
+    }
+
     case ArgValueType::UNSIGNED: {
       // boost::lexical_cast<uint64_t> will accept negative number
       int64_t v = boost::lexical_cast<int64_t>(token);
       if (v < 0) {
-        BOOST_THROW_EXCEPTION(std::out_of_range("value is negative"));
+        BOOST_THROW_EXCEPTION(std::out_of_range("value '" + token + "' is negative"));
       }
       return static_cast<uint64_t>(v);
     }
diff --git a/tools/nfdc/command-definition.hpp b/tools/nfdc/command-definition.hpp
index 7d8c8f0..5661ff2 100644
--- a/tools/nfdc/command-definition.hpp
+++ b/tools/nfdc/command-definition.hpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -49,6 +49,12 @@
    */
   ANY,
 
+  /** \brief boolean
+   *
+   * The argument appears in CommandArguments as bool.
+   */
+  BOOLEAN,
+
   /** \brief non-negative integer
    *
    *  The argument appears in CommandArguments as uint64_t.
diff --git a/tools/nfdc/face-module.cpp b/tools/nfdc/face-module.cpp
index 39ca5c2..1a88229 100644
--- a/tools/nfdc/face-module.cpp
+++ b/tools/nfdc/face-module.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -53,7 +53,8 @@
     .setTitle("create a face")
     .addArg("remote", ArgValueType::FACE_URI, Required::YES, Positional::YES)
     .addArg("persistency", ArgValueType::FACE_PERSISTENCY, Required::NO, Positional::YES)
-    .addArg("local", ArgValueType::FACE_URI, Required::NO, Positional::NO);
+    .addArg("local", ArgValueType::FACE_URI, Required::NO, Positional::NO)
+    .addArg("reliability", ArgValueType::BOOLEAN, Required::NO, Positional::NO);
   parser.addCommand(defFaceCreate, &FaceModule::create);
 
   CommandDefinition defFaceDestroy("face", "destroy");
@@ -151,6 +152,8 @@
   auto remoteUri = ctx.args.get<FaceUri>("remote");
   auto localUri = ctx.args.getOptional<FaceUri>("local");
   auto persistency = ctx.args.get<FacePersistency>("persistency", FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+  auto lpReliability = ctx.args.getTribool("reliability");
+
   FaceUri canonicalRemote;
   ndn::optional<FaceUri> canonicalLocal;
 
@@ -166,9 +169,17 @@
             << ia("local") << resp.getLocalUri()
             << ia("remote") << resp.getUri()
             << ia("persistency") << resp.getFacePersistency()
+            << ia("reliability") << (resp.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED) ? "on" : "off")
             << '\n';
   };
 
+  auto updateFace = [&printPositiveResult] (ControlParameters respParams, ControlParameters resp) {
+    // faces/update response does not have FaceUris, copy from faces/create response
+    resp.setLocalUri(respParams.getLocalUri())
+        .setUri(respParams.getUri());
+    printPositiveResult("face-updated", resp);
+  };
+
   auto handle409 = [&] (const ControlResponse& resp) {
     ControlParameters respParams(resp.getBody());
     if (respParams.getUri() != canonicalRemote.toString()) {
@@ -178,19 +189,39 @@
 
     if (persistencyLessThan(respParams.getFacePersistency(), persistency)) {
       // need to upgrade persistency
+      ControlParameters params;
+      params.setFaceId(respParams.getFaceId()).setFacePersistency(persistency);
+      if (!boost::logic::indeterminate(lpReliability)) {
+        params.setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, lpReliability);
+      }
       ctx.controller.start<ndn::nfd::FaceUpdateCommand>(
-          ControlParameters().setFaceId(respParams.getFaceId()).setFacePersistency(persistency),
-          [respParams, &printPositiveResult] (ControlParameters resp2) {
-            // faces/update response does not have FaceUris, copy from faces/create response
-            resp2.setLocalUri(respParams.getLocalUri())
-                 .setUri(respParams.getUri());
-            printPositiveResult("face-updated", resp2);
-          },
+          params,
+          bind(updateFace, respParams, _1),
           ctx.makeCommandFailureHandler("upgrading face persistency"),
           ctx.makeCommandOptions());
     }
+    else if (lpReliability && !respParams.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED)) {
+      // enable reliability
+      ControlParameters params;
+      params.setFaceId(respParams.getFaceId()).setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, true);
+      ctx.controller.start<ndn::nfd::FaceUpdateCommand>(
+          params,
+          bind(updateFace, respParams, _1),
+          ctx.makeCommandFailureHandler("enabling reliability"),
+          ctx.makeCommandOptions());
+    }
+    else if (!lpReliability && respParams.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED)) {
+      // disable reliability
+      ControlParameters params;
+      params.setFaceId(respParams.getFaceId()).setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, false);
+      ctx.controller.start<ndn::nfd::FaceUpdateCommand>(
+          params,
+          bind(updateFace, respParams, _1),
+          ctx.makeCommandFailureHandler("disabling reliability"),
+          ctx.makeCommandOptions());
+    }
     else {
-      // don't downgrade persistency
+      // don't do anything
       printPositiveResult("face-exists", respParams);
     }
     return true;
@@ -203,6 +234,9 @@
       params.setLocalUri(canonicalLocal->toString());
     }
     params.setFacePersistency(persistency);
+    if (!boost::logic::indeterminate(lpReliability)) {
+      params.setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, lpReliability);
+    }
 
     ctx.controller.start<ndn::nfd::FaceCreateCommand>(
       params,
@@ -275,7 +309,9 @@
       ctx.out << ia("id") << face.getFaceId()
               << ia("local") << face.getLocalUri()
               << ia("remote") << face.getRemoteUri()
-              << ia("persistency") << face.getFacePersistency() << '\n';
+              << ia("persistency") << face.getFacePersistency()
+              << ia("reliability") << (resp.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED) ? "on" : "off")
+              << '\n';
     },
     ctx.makeCommandFailureHandler("destroying face"),
     ctx.makeCommandOptions());
@@ -332,6 +368,9 @@
     if (item.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED)) {
       os << "<localFieldsEnabled/>";
     }
+    if (item.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED)) {
+      os << "<lpReliabilityEnabled/>";
+    }
     os << "</flags>";
   }
 
@@ -400,6 +439,9 @@
   if (item.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED)) {
     os << flagSep << "local-fields";
   }
+  if (item.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED)) {
+    os << flagSep << "lp-reliability";
+  }
   os << '}';
 
   os << ia.end();
diff --git a/tools/nfdc/status-report.cpp b/tools/nfdc/status-report.cpp
index ff3e8a6..01ad5c6 100644
--- a/tools/nfdc/status-report.cpp
+++ b/tools/nfdc/status-report.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+/*
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -39,7 +39,7 @@
   if (s == "text") {
     return ReportFormat::TEXT;
   }
-  BOOST_THROW_EXCEPTION(std::invalid_argument("unrecognized ReportFormat"));
+  BOOST_THROW_EXCEPTION(std::invalid_argument("unrecognized ReportFormat '" + s + "'"));
 }
 
 std::ostream&
@@ -64,7 +64,7 @@
     Module& module = *sections[i];
     module.fetchStatus(
       controller,
-      [] {},
+      []{},
       [i, &errorCode] (uint32_t code, const std::string& reason) {
         errorCode = i * 1000000 + code;
       },