management: NFD RIB Management

refs #1502

Change-Id: I4a63f2fe17e23939fb6684be2d80e66671afcf6c
diff --git a/src/encoding/tlv-nfd.hpp b/src/encoding/tlv-nfd.hpp
index b72b731..b3466bb 100644
--- a/src/encoding/tlv-nfd.hpp
+++ b/src/encoding/tlv-nfd.hpp
@@ -23,8 +23,11 @@
   FaceId              = 105,
   Uri                 = 114,
   LocalControlFeature = 110,
+  Origin              = 111,
   Cost                = 106,
+  Flags               = 108,
   Strategy            = 107,
+  ExpirationPeriod    = 109,
 
   // ControlResponse
   ControlResponse = 101,
diff --git a/src/encoding/tlv-nrd.hpp b/src/encoding/tlv-nrd.hpp
index 9ca1a65..5dc00a0 100644
--- a/src/encoding/tlv-nrd.hpp
+++ b/src/encoding/tlv-nrd.hpp
@@ -16,6 +16,7 @@
 namespace tlv {
 namespace nrd {
 
+// \deprecated use NFD RIB Management
 enum {
   PrefixRegOptions = 101,
   FaceId           = 102,
diff --git a/src/management/nfd-control-command.hpp b/src/management/nfd-control-command.hpp
index c1cb438..8d08013 100644
--- a/src/management/nfd-control-command.hpp
+++ b/src/management/nfd-control-command.hpp
@@ -97,9 +97,9 @@
   {
   public:
     FieldValidator()
+      : m_required(CONTROL_PARAMETER_UBOUND)
+      , m_optional(CONTROL_PARAMETER_UBOUND)
     {
-      m_required.resize(CONTROL_PARAMETER_UBOUND);
-      m_optional.resize(CONTROL_PARAMETER_UBOUND);
     }
 
     /** \brief declare a required field
@@ -401,6 +401,123 @@
   }
 };
 
+
+enum {
+  // route origin
+  ROUTE_ORIGIN_APP    = 0,
+  ROUTE_ORIGIN_NLSR   = 128,
+  ROUTE_ORIGIN_STATIC = 255,
+
+  // route inheritance flags
+  ROUTE_FLAG_CHILD_INHERIT = 1,
+  ROUTE_FLAG_CAPTURE       = 2
+};
+
+
+/** \brief represents a rib/register command
+ *  \sa http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#Register-a-route
+ */
+class RibRegisterCommand : public ControlCommand
+{
+public:
+  RibRegisterCommand()
+    : ControlCommand("rib", "register")
+  {
+    m_requestValidator
+      .required(CONTROL_PARAMETER_NAME)
+      .optional(CONTROL_PARAMETER_FACE_ID)
+      .optional(CONTROL_PARAMETER_ORIGIN)
+      .optional(CONTROL_PARAMETER_COST)
+      .optional(CONTROL_PARAMETER_FLAGS)
+      .optional(CONTROL_PARAMETER_EXPIRATION_PERIOD);
+    m_responseValidator
+      .required(CONTROL_PARAMETER_NAME)
+      .required(CONTROL_PARAMETER_FACE_ID)
+      .required(CONTROL_PARAMETER_ORIGIN)
+      .required(CONTROL_PARAMETER_COST)
+      .required(CONTROL_PARAMETER_FLAGS)
+      .required(CONTROL_PARAMETER_EXPIRATION_PERIOD);
+  }
+
+  virtual void
+  applyDefaultsToRequest(ControlParameters& parameters) const
+  {
+    if (!parameters.hasFaceId()) {
+      parameters.setFaceId(0);
+    }
+    if (!parameters.hasOrigin()) {
+      parameters.setOrigin(ROUTE_ORIGIN_APP);
+    }
+    if (!parameters.hasCost()) {
+      parameters.setCost(0);
+    }
+    if (!parameters.hasFlags()) {
+      parameters.setFlags(ROUTE_FLAG_CHILD_INHERIT);
+    }
+    if (!parameters.hasExpirationPeriod()) {
+      if (parameters.getFaceId() == 0) {
+        parameters.setExpirationPeriod(time::milliseconds::max());
+      }
+      else {
+        parameters.setExpirationPeriod(time::hours(1));
+      }
+    }
+  }
+
+  virtual void
+  validateResponse(const ControlParameters& parameters) const
+  {
+    this->ControlCommand::validateResponse(parameters);
+
+    if (parameters.getFaceId() == 0) {
+      throw ArgumentError("FaceId must not be zero");
+    }
+  }
+};
+
+
+/** \brief represents a rib/unregister command
+ *  \sa http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#Unregister-a-route
+ */
+class RibUnregisterCommand : public ControlCommand
+{
+public:
+  RibUnregisterCommand()
+    : ControlCommand("rib", "unregister")
+  {
+    m_requestValidator
+      .required(CONTROL_PARAMETER_NAME)
+      .optional(CONTROL_PARAMETER_FACE_ID)
+      .optional(CONTROL_PARAMETER_ORIGIN);
+    m_responseValidator
+      .required(CONTROL_PARAMETER_NAME)
+      .required(CONTROL_PARAMETER_FACE_ID)
+      .required(CONTROL_PARAMETER_ORIGIN);
+  }
+
+  virtual void
+  applyDefaultsToRequest(ControlParameters& parameters) const
+  {
+    if (!parameters.hasFaceId()) {
+      parameters.setFaceId(0);
+    }
+    if (!parameters.hasOrigin()) {
+      parameters.setOrigin(ROUTE_ORIGIN_APP);
+    }
+  }
+
+  virtual void
+  validateResponse(const ControlParameters& parameters) const
+  {
+    this->ControlCommand::validateResponse(parameters);
+
+    if (parameters.getFaceId() == 0) {
+      throw ArgumentError("FaceId must not be zero");
+    }
+  }
+};
+
+
 } // namespace nfd
 } // namespace ndn
 
diff --git a/src/management/nfd-control-parameters.hpp b/src/management/nfd-control-parameters.hpp
index 0eff8ae..f0aa1b5 100644
--- a/src/management/nfd-control-parameters.hpp
+++ b/src/management/nfd-control-parameters.hpp
@@ -18,8 +18,11 @@
   CONTROL_PARAMETER_FACE_ID,
   CONTROL_PARAMETER_URI,
   CONTROL_PARAMETER_LOCAL_CONTROL_FEATURE,
+  CONTROL_PARAMETER_ORIGIN,
   CONTROL_PARAMETER_COST,
+  CONTROL_PARAMETER_FLAGS,
   CONTROL_PARAMETER_STRATEGY,
+  CONTROL_PARAMETER_EXPIRATION_PERIOD,
   CONTROL_PARAMETER_UBOUND
 };
 
@@ -28,8 +31,11 @@
   "FaceId",
   "Uri",
   "LocalControlFeature",
+  "Origin",
   "Cost",
+  "Flags",
   "Strategy",
+  "ExpirationPeriod",
 };
 
 enum LocalControlFeature {
@@ -76,6 +82,7 @@
   wireDecode(const Block& wire);
 
 public: // getters & setters
+
   bool
   hasName() const
   {
@@ -197,6 +204,36 @@
   }
 
   bool
+  hasOrigin() const
+  {
+    return m_hasFields[CONTROL_PARAMETER_ORIGIN];
+  }
+
+  uint64_t
+  getOrigin() const
+  {
+    BOOST_ASSERT(this->hasOrigin());
+    return m_origin;
+  }
+
+  ControlParameters&
+  setOrigin(uint64_t origin)
+  {
+    m_wire.reset();
+    m_origin = origin;
+    m_hasFields[CONTROL_PARAMETER_ORIGIN] = true;
+    return *this;
+  }
+
+  ControlParameters&
+  unsetOrigin()
+  {
+    m_wire.reset();
+    m_hasFields[CONTROL_PARAMETER_ORIGIN] = false;
+    return *this;
+  }
+
+  bool
   hasCost() const
   {
     return m_hasFields[CONTROL_PARAMETER_COST];
@@ -227,6 +264,36 @@
   }
 
   bool
+  hasFlags() const
+  {
+    return m_hasFields[CONTROL_PARAMETER_FLAGS];
+  }
+
+  uint64_t
+  getFlags() const
+  {
+    BOOST_ASSERT(this->hasFlags());
+    return m_flags;
+  }
+
+  ControlParameters&
+  setFlags(uint64_t flags)
+  {
+    m_wire.reset();
+    m_flags = flags;
+    m_hasFields[CONTROL_PARAMETER_FLAGS] = true;
+    return *this;
+  }
+
+  ControlParameters&
+  unsetFlags()
+  {
+    m_wire.reset();
+    m_hasFields[CONTROL_PARAMETER_FLAGS] = false;
+    return *this;
+  }
+
+  bool
   hasStrategy() const
   {
     return m_hasFields[CONTROL_PARAMETER_STRATEGY];
@@ -256,6 +323,36 @@
     return *this;
   }
 
+  bool
+  hasExpirationPeriod() const
+  {
+    return m_hasFields[CONTROL_PARAMETER_EXPIRATION_PERIOD];
+  }
+
+  const time::milliseconds&
+  getExpirationPeriod() const
+  {
+    BOOST_ASSERT(this->hasExpirationPeriod());
+    return m_expirationPeriod;
+  }
+
+  ControlParameters&
+  setExpirationPeriod(const time::milliseconds& expirationPeriod)
+  {
+    m_wire.reset();
+    m_expirationPeriod = expirationPeriod;
+    m_hasFields[CONTROL_PARAMETER_EXPIRATION_PERIOD] = true;
+    return *this;
+  }
+
+  ControlParameters&
+  unsetExpirationPeriod()
+  {
+    m_wire.reset();
+    m_hasFields[CONTROL_PARAMETER_EXPIRATION_PERIOD] = false;
+    return *this;
+  }
+
   const std::vector<bool>&
   getPresentFields() const
   {
@@ -269,8 +366,11 @@
   uint64_t            m_faceId;
   std::string         m_uri;
   LocalControlFeature m_localControlFeature;
+  uint64_t            m_origin;
   uint64_t            m_cost;
+  uint64_t            m_flags;
   Name                m_strategy;
+  time::milliseconds  m_expirationPeriod;
 
 private:
   mutable Block m_wire;
@@ -283,12 +383,22 @@
 {
   size_t totalLength = 0;
 
+  if (this->hasExpirationPeriod()) {
+    totalLength += prependNonNegativeIntegerBlock(encoder,
+                   tlv::nfd::ExpirationPeriod, m_expirationPeriod.count());
+  }
   if (this->hasStrategy()) {
     totalLength += prependNestedBlock(encoder, tlv::nfd::Strategy, m_strategy);
   }
+  if (this->hasFlags()) {
+    totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::Flags, m_flags);
+  }
   if (this->hasCost()) {
     totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::Cost, m_cost);
   }
+  if (this->hasOrigin()) {
+    totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::Origin, m_origin);
+  }
   if (this->hasLocalControlFeature()) {
     totalLength += prependNonNegativeIntegerBlock(encoder,
                    tlv::nfd::LocalControlFeature, m_localControlFeature);
@@ -362,12 +472,24 @@
     m_localControlFeature = static_cast<LocalControlFeature>(readNonNegativeInteger(*val));
   }
 
+  val = m_wire.find(tlv::nfd::Origin);
+  m_hasFields[CONTROL_PARAMETER_ORIGIN] = val != m_wire.elements_end();
+  if (this->hasOrigin()) {
+    m_origin = static_cast<uint64_t>(readNonNegativeInteger(*val));
+  }
+
   val = m_wire.find(tlv::nfd::Cost);
   m_hasFields[CONTROL_PARAMETER_COST] = val != m_wire.elements_end();
   if (this->hasCost()) {
     m_cost = static_cast<uint64_t>(readNonNegativeInteger(*val));
   }
 
+  val = m_wire.find(tlv::nfd::Flags);
+  m_hasFields[CONTROL_PARAMETER_FLAGS] = val != m_wire.elements_end();
+  if (this->hasFlags()) {
+    m_flags = static_cast<uint64_t>(readNonNegativeInteger(*val));
+  }
+
   val = m_wire.find(tlv::nfd::Strategy);
   m_hasFields[CONTROL_PARAMETER_STRATEGY] = val != m_wire.elements_end();
   if (this->hasStrategy()) {
@@ -379,6 +501,12 @@
       m_strategy.wireDecode(*val->elements_begin());
     }
   }
+
+  val = m_wire.find(tlv::nfd::ExpirationPeriod);
+  m_hasFields[CONTROL_PARAMETER_EXPIRATION_PERIOD] = val != m_wire.elements_end();
+  if (this->hasExpirationPeriod()) {
+    m_expirationPeriod = time::milliseconds(readNonNegativeInteger(*val));
+  }
 }
 
 inline std::ostream&
@@ -402,14 +530,26 @@
     os << "LocalControlFeature: " << parameters.getLocalControlFeature() << ", ";
   }
 
+  if (parameters.hasOrigin()) {
+    os << "Origin: " << parameters.getOrigin() << ", ";
+  }
+
   if (parameters.hasCost()) {
     os << "Cost: " << parameters.getCost() << ", ";
   }
 
+  if (parameters.hasFlags()) {
+    os << "Flags: " << parameters.getFlags() << ", ";
+  }
+
   if (parameters.hasStrategy()) {
     os << "Strategy: " << parameters.getStrategy() << ", ";
   }
 
+  if (parameters.hasExpirationPeriod()) {
+    os << "ExpirationPeriod: " << parameters.getExpirationPeriod() << ", ";
+  }
+
   os << ")";
   return os;
 }
diff --git a/src/management/nfd-controller.hpp b/src/management/nfd-controller.hpp
index 823169a..1b46c76 100644
--- a/src/management/nfd-controller.hpp
+++ b/src/management/nfd-controller.hpp
@@ -38,7 +38,7 @@
         const CommandSucceedCallback& onSuccess,
         const CommandFailCallback& onFailure);
 
-public: // selfreg
+public: // selfreg using FIB Management commands
   virtual void
   selfRegisterPrefix(const Name& prefixToRegister,
                      const SuccessCallback& onSuccess,
diff --git a/src/management/nrd-controller.cpp b/src/management/nrd-controller.cpp
index 66156d2..bf99148 100644
--- a/src/management/nrd-controller.cpp
+++ b/src/management/nrd-controller.cpp
@@ -4,21 +4,19 @@
  * See COPYING for copyright and distribution information.
  */
 
-#include "common.hpp"
-#include "../face.hpp"
-
-// NRD
 #include "nrd-controller.hpp"
 #include "nrd-prefix-reg-options.hpp"
-
-// NFD
-#include "nfd-control-response.hpp"
+#include "nfd-control-response.hpp" // used in deprecated function only
 
 namespace ndn {
 namespace nrd {
 
+using nfd::ControlParameters;
+using nfd::RibRegisterCommand;
+using nfd::RibUnregisterCommand;
+
 Controller::Controller(Face& face)
-  : m_face(face)
+  : nfd::Controller(face)
 {
 }
 
@@ -27,12 +25,12 @@
                                const SuccessCallback& onSuccess,
                                const FailCallback&    onFail)
 {
-  startCommand("register",
-               PrefixRegOptions()
-                 .setName(prefixToRegister)
-                 .setFaceId(0) // self-registration
-                 .setCost(0),
-               bind(onSuccess), onFail);
+  ControlParameters parameters;
+  parameters.setName(prefixToRegister);
+
+  this->start<RibRegisterCommand>(parameters,
+                                  bind(onSuccess),
+                                  bind(onFail, _2));
 }
 
 void
@@ -40,11 +38,12 @@
                                  const SuccessCallback& onSuccess,
                                  const FailCallback&    onFail)
 {
-  startCommand("unregister",
-               PrefixRegOptions()
-                 .setName(prefixToRegister)
-                 .setFaceId(0), // self-registration
-               bind(onSuccess), onFail);
+  ControlParameters parameters;
+  parameters.setName(prefixToRegister);
+
+  this->start<RibUnregisterCommand>(parameters,
+                                    bind(onSuccess),
+                                    bind(onFail, _2));
 }
 
 void
diff --git a/src/management/nrd-controller.hpp b/src/management/nrd-controller.hpp
index de64325..5b24378 100644
--- a/src/management/nrd-controller.hpp
+++ b/src/management/nrd-controller.hpp
@@ -7,24 +7,24 @@
 #ifndef NDN_MANAGEMENT_NRD_CONTROLLER_HPP
 #define NDN_MANAGEMENT_NRD_CONTROLLER_HPP
 
-#include "controller.hpp"
-#include "../util/command-interest-generator.hpp"
+#include "nfd-controller.hpp"
 
 namespace ndn {
 namespace nrd {
 
+/// \deprecated
 class PrefixRegOptions;
 
-class Controller : public ndn::Controller
+class Controller : public nfd::Controller
 {
 public:
+  /// \deprecated
   typedef function<void(const PrefixRegOptions&)> CommandSucceedCallback;
 
-  /**
-   * @brief Construct ndnd::Control object
-   */
+  explicit
   Controller(Face& face);
 
+public: // selfreg using RIB Management commands
   virtual void
   selfRegisterPrefix(const Name& prefixToRegister,
                      const SuccessCallback& onSuccess,
@@ -35,27 +35,33 @@
                        const SuccessCallback& onSuccess,
                        const FailCallback&    onFail);
 
+public:
+  /// \deprecated .start<RibRegisterCommand>
   void
   registerPrefix(const PrefixRegOptions& options,
                  const CommandSucceedCallback& onSuccess,
                  const FailCallback& onFail);
 
+  /// \deprecated .start<RibUnregisterCommand>
   void
   unregisterPrefix(const PrefixRegOptions& options,
                  const CommandSucceedCallback& onSuccess,
                  const FailCallback&    onFail);
 
+  /// \deprecated
   void
   advertisePrefix(const PrefixRegOptions& options,
                   const CommandSucceedCallback& onSuccess,
                   const FailCallback& onFail);
 
+  /// \deprecated
   void
   withdrawPrefix(const PrefixRegOptions& options,
                  const CommandSucceedCallback& onSuccess,
                  const FailCallback& onFail);
 
 protected:
+  /// \deprecated
   void
   startCommand(const std::string& command,
                const PrefixRegOptions& options,
@@ -63,14 +69,11 @@
                const FailCallback& onFailure);
 
 private:
+  /// \deprecated
   void
   processCommandResponse(Data& data,
                          const CommandSucceedCallback& onSuccess,
                          const FailCallback& onFail);
-
-protected:
-  Face& m_face;
-  CommandInterestGenerator m_commandInterestGenerator;
 };
 
 } // namespace nrd
diff --git a/src/management/nrd-prefix-reg-options.hpp b/src/management/nrd-prefix-reg-options.hpp
index 2c37c4a..ea8ca4d 100644
--- a/src/management/nrd-prefix-reg-options.hpp
+++ b/src/management/nrd-prefix-reg-options.hpp
@@ -23,6 +23,7 @@
  * @brief Abstraction for prefix registration options for NRD Prefix registration protocol
  *
  * @see http://redmine.named-data.net/projects/nrd/wiki/NRD_Prefix_Registration_protocol
+ * @deprecated use NFD RIB Management
  */
 class PrefixRegOptions {
 public:
diff --git a/tests/management/test-nfd-control-command.cpp b/tests/management/test-nfd-control-command.cpp
index 36d19e0..00fe2cf 100644
--- a/tests/management/test-nfd-control-command.cpp
+++ b/tests/management/test-nfd-control-command.cpp
@@ -181,6 +181,60 @@
   BOOST_CHECK_THROW(command.validateResponse(p3), ControlCommand::ArgumentError);
 }
 
+BOOST_AUTO_TEST_CASE(RibRegister)
+{
+  RibRegisterCommand command;
+  BOOST_CHECK_EQUAL(command.getPrefix(), "ndn:/localhost/nfd/rib/register");
+
+  ControlParameters p1;
+  p1.setName("ndn:/");
+  BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+  BOOST_CHECK_THROW(command.validateResponse(p1), ControlCommand::ArgumentError);
+
+  command.applyDefaultsToRequest(p1);
+  BOOST_REQUIRE(p1.hasOrigin());
+  BOOST_CHECK_EQUAL(p1.getOrigin(), static_cast<uint64_t>(ROUTE_ORIGIN_APP));
+  BOOST_REQUIRE(p1.hasCost());
+  BOOST_CHECK_EQUAL(p1.getCost(), 0);
+  BOOST_REQUIRE(p1.hasFlags());
+  BOOST_CHECK_EQUAL(p1.getFlags(), static_cast<uint64_t>(ROUTE_FLAG_CHILD_INHERIT));
+  BOOST_REQUIRE(p1.hasExpirationPeriod());
+  BOOST_CHECK_GT(p1.getExpirationPeriod(), time::hours(240));
+
+  ControlParameters p2;
+  p2.setName("ndn:/example")
+    .setFaceId(2)
+    .setCost(6);
+  BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+  command.applyDefaultsToRequest(p2);
+  BOOST_CHECK_EQUAL(p2.getExpirationPeriod(), time::hours(1));
+  BOOST_CHECK_NO_THROW(command.validateResponse(p2));
+}
+
+BOOST_AUTO_TEST_CASE(RibUnregister)
+{
+  RibUnregisterCommand command;
+  BOOST_CHECK_EQUAL(command.getPrefix(), "ndn:/localhost/nfd/rib/unregister");
+
+  ControlParameters p1;
+  p1.setName("ndn:/")
+    .setFaceId(22)
+    .setOrigin(ROUTE_ORIGIN_STATIC);
+  BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+  BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+
+  ControlParameters p2;
+  p2.setName("ndn:/example")
+    .setFaceId(0)
+    .setOrigin(ROUTE_ORIGIN_APP);
+  BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+  BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+  p2.unsetFaceId();
+  BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+  BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace nfd
diff --git a/tests/management/test-nfd-control-parameters.cpp b/tests/management/test-nfd-control-parameters.cpp
index e0733a3..53751cd 100644
--- a/tests/management/test-nfd-control-parameters.cpp
+++ b/tests/management/test-nfd-control-parameters.cpp
@@ -22,11 +22,15 @@
 
   ControlParameters decoded(wire);
   BOOST_CHECK_EQUAL(decoded.getUri(), "tcp4://192.0.2.1:6363");
+
   BOOST_CHECK_EQUAL(decoded.hasName(), false);
   BOOST_CHECK_EQUAL(decoded.hasFaceId(), false);
   BOOST_CHECK_EQUAL(decoded.hasLocalControlFeature(), false);
+  BOOST_CHECK_EQUAL(decoded.hasOrigin(), false);
   BOOST_CHECK_EQUAL(decoded.hasCost(), false);
+  BOOST_CHECK_EQUAL(decoded.hasFlags(), false);
   BOOST_CHECK_EQUAL(decoded.hasStrategy(), false);
+  BOOST_CHECK_EQUAL(decoded.hasExpirationPeriod(), false);
 }
 
 BOOST_AUTO_TEST_CASE(FaceLocalControlOptions)
@@ -38,11 +42,15 @@
 
   ControlParameters decoded(wire);
   BOOST_CHECK_EQUAL(decoded.getLocalControlFeature(), LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+
   BOOST_CHECK_EQUAL(decoded.hasName(), false);
   BOOST_CHECK_EQUAL(decoded.hasFaceId(), false);
-  BOOST_CHECK_EQUAL(decoded.hasCost(), false);
   BOOST_CHECK_EQUAL(decoded.hasUri(), false);
+  BOOST_CHECK_EQUAL(decoded.hasOrigin(), false);
+  BOOST_CHECK_EQUAL(decoded.hasCost(), false);
+  BOOST_CHECK_EQUAL(decoded.hasFlags(), false);
   BOOST_CHECK_EQUAL(decoded.hasStrategy(), false);
+  BOOST_CHECK_EQUAL(decoded.hasExpirationPeriod(), false);
 }
 
 BOOST_AUTO_TEST_CASE(FibOptions)
@@ -58,9 +66,13 @@
   BOOST_CHECK_EQUAL(decoded.getName(), Name("ndn:/example"));
   BOOST_CHECK_EQUAL(decoded.getFaceId(), 4);
   BOOST_CHECK_EQUAL(decoded.getCost(), 555);
-  BOOST_CHECK_EQUAL(decoded.hasLocalControlFeature(), false);
+
   BOOST_CHECK_EQUAL(decoded.hasUri(), false);
+  BOOST_CHECK_EQUAL(decoded.hasLocalControlFeature(), false);
+  BOOST_CHECK_EQUAL(decoded.hasOrigin(), false);
+  BOOST_CHECK_EQUAL(decoded.hasFlags(), false);
   BOOST_CHECK_EQUAL(decoded.hasStrategy(), false);
+  BOOST_CHECK_EQUAL(decoded.hasExpirationPeriod(), false);
 }
 
 BOOST_AUTO_TEST_CASE(StrategyChoiceOptions)
@@ -74,10 +86,39 @@
   ControlParameters decoded(wire);
   BOOST_CHECK_EQUAL(decoded.getName(), Name("ndn:/"));
   BOOST_CHECK_EQUAL(decoded.getStrategy(), Name("ndn:/strategy/A"));
+
   BOOST_CHECK_EQUAL(decoded.hasFaceId(), false);
   BOOST_CHECK_EQUAL(decoded.hasUri(), false);
   BOOST_CHECK_EQUAL(decoded.hasLocalControlFeature(), false);
+  BOOST_CHECK_EQUAL(decoded.hasOrigin(), false);
   BOOST_CHECK_EQUAL(decoded.hasCost(), false);
+  BOOST_CHECK_EQUAL(decoded.hasFlags(), false);
+  BOOST_CHECK_EQUAL(decoded.hasExpirationPeriod(), false);
+}
+
+BOOST_AUTO_TEST_CASE(RibOptions)
+{
+  ControlParameters parameters;
+  parameters.setName("ndn:/example")
+            .setFaceId(4)
+            .setOrigin(128)
+            .setCost(6)
+            .setFlags(0x01)
+            .setExpirationPeriod(time::milliseconds(1800000));
+
+  Block wire = parameters.wireEncode();
+
+  ControlParameters decoded(wire);
+  BOOST_CHECK_EQUAL(decoded.getName(), Name("ndn:/example"));
+  BOOST_CHECK_EQUAL(decoded.getFaceId(), 4);
+  BOOST_CHECK_EQUAL(decoded.getOrigin(), 128);
+  BOOST_CHECK_EQUAL(decoded.getCost(), 6);
+  BOOST_CHECK_EQUAL(decoded.getFlags(), 0x01);
+  BOOST_CHECK_EQUAL(decoded.getExpirationPeriod(), time::milliseconds(1800000));
+
+  BOOST_CHECK_EQUAL(decoded.hasUri(), false);
+  BOOST_CHECK_EQUAL(decoded.hasLocalControlFeature(), false);
+  BOOST_CHECK_EQUAL(decoded.hasStrategy(), false);
 }
 
 BOOST_AUTO_TEST_SUITE_END()