mgmt: support rib/announce control command
This commit adds support for a new control command type,
RibAnnounceCommand. A new command format class is also added,
ApplicationParametersCommandFormat, to support encoding the
request information in the ApplicationParameters element of
the interest instead of using ControlParameters.
Refs: #4650
Change-Id: I351d3852062a80349fb67d7f18db4c883551835c
diff --git a/ndn-cxx/mgmt/nfd/control-command.cpp b/ndn-cxx/mgmt/nfd/control-command.cpp
index 31ec2df..6e90f84 100644
--- a/ndn-cxx/mgmt/nfd/control-command.cpp
+++ b/ndn-cxx/mgmt/nfd/control-command.cpp
@@ -347,4 +347,42 @@
}
}
+void
+RibAnnounceParameters::wireDecode(const Block& wire)
+{
+ wire.parse();
+ auto it = wire.find(tlv::Data);
+ if (it == wire.elements_end()) {
+ NDN_THROW(Error("Missing prefix announcement parameter"));
+ }
+ m_prefixAnn = PrefixAnnouncement(Data(*it));
+}
+
+Block
+RibAnnounceParameters::wireEncode() const
+{
+ if (!m_prefixAnn.getData()) {
+ NDN_THROW(Error("Prefix announcement must be signed"));
+ }
+ return m_prefixAnn.getData()->wireEncode();
+}
+
+const RibAnnounceCommand::RequestFormat RibAnnounceCommand::s_requestFormat;
+const RibAnnounceCommand::ResponseFormat RibAnnounceCommand::s_responseFormat =
+ RibRegisterCommand::s_responseFormat;
+
+void
+RibAnnounceCommand::validateRequestImpl(const RibAnnounceParameters& parameters)
+{
+ if (!parameters.getPrefixAnnouncement().getData()) {
+ NDN_THROW(ArgumentError("Prefix announcement must be signed"));
+ }
+}
+
+void
+RibAnnounceCommand::validateResponseImpl(const ControlParameters& parameters)
+{
+ RibRegisterCommand::validateResponseImpl(parameters);
+}
+
} // namespace ndn::nfd
diff --git a/ndn-cxx/mgmt/nfd/control-command.hpp b/ndn-cxx/mgmt/nfd/control-command.hpp
index a67e28e..a6784c7 100644
--- a/ndn-cxx/mgmt/nfd/control-command.hpp
+++ b/ndn-cxx/mgmt/nfd/control-command.hpp
@@ -24,6 +24,7 @@
#include "ndn-cxx/interest.hpp"
#include "ndn-cxx/mgmt/nfd/control-parameters.hpp"
+#include "ndn-cxx/prefix-announcement.hpp"
#include <bitset>
@@ -103,9 +104,52 @@
/**
* \ingroup management
+ * \brief Implements decoding, encoding, and validation of control command parameters carried
+ * in the ApplicationParameters of the request (Interest packet).
+ * \note This format is applicable to control command requests only.
+ */
+template<typename PT>
+class ApplicationParametersCommandFormat
+{
+public:
+ using ParametersType = PT;
+
+ /**
+ * \brief Does nothing.
+ */
+ void
+ validate(const ParametersType&) const
+ {
+ }
+
+ /**
+ * \brief Extract the parameters from the request \p interest.
+ */
+ static shared_ptr<ParametersType>
+ decode(const Interest& interest, size_t prefixLen = 0)
+ {
+ auto params = make_shared<ParametersType>();
+ params->wireDecode(interest.getApplicationParameters());
+ return params;
+ }
+
+ /**
+ * \brief Serialize the parameters into the request \p interest.
+ */
+ static void
+ encode(Interest& interest, const ParametersType& params)
+ {
+ interest.setApplicationParameters(params.wireEncode());
+ }
+};
+
+
+/**
+ * \ingroup management
* \brief Base class for all NFD control commands.
* \tparam RequestFormatType A class type that will handle the encoding and validation of the request
- * parameters. Only ControlParametersCommandFormat is supported for now.
+ * parameters. The type can be ApplicationParametersCommandFormat or
+ * ControlParametersCommandFormat.
* \tparam ResponseFormatType A class type that will handle the encoding and validation of the response
* parameters. Only ControlParametersCommandFormat is supported for now.
* \sa https://redmine.named-data.net/projects/nfd/wiki/ControlCommand
@@ -384,6 +428,7 @@
class RibRegisterCommand : public ControlCommand<RibRegisterCommand>
{
NDN_CXX_CONTROL_COMMAND("rib", "register");
+ friend class RibAnnounceCommand;
static void
applyDefaultsToRequestImpl(ControlParameters& parameters);
@@ -409,6 +454,61 @@
validateResponseImpl(const ControlParameters& parameters);
};
+
+/**
+ * \ingroup management
+ * \brief Request parameters for `rib/announce` command.
+ */
+class RibAnnounceParameters final : public mgmt::ControlParametersBase
+{
+public:
+ class Error : public tlv::Error
+ {
+ public:
+ using tlv::Error::Error;
+ };
+
+ const PrefixAnnouncement&
+ getPrefixAnnouncement() const
+ {
+ return m_prefixAnn;
+ }
+
+ RibAnnounceParameters&
+ setPrefixAnnouncement(const PrefixAnnouncement& pa)
+ {
+ m_prefixAnn = pa;
+ return *this;
+ }
+
+ void
+ wireDecode(const Block& wire) final;
+
+ Block
+ wireEncode() const final;
+
+private:
+ PrefixAnnouncement m_prefixAnn;
+};
+
+
+/**
+ * \ingroup management
+ * \brief Represents a `rib/announce` command.
+ * \sa https://redmine.named-data.net/projects/nfd/wiki/PrefixAnnouncement
+ */
+class RibAnnounceCommand : public ControlCommand<RibAnnounceCommand,
+ ApplicationParametersCommandFormat<RibAnnounceParameters>>
+{
+ NDN_CXX_CONTROL_COMMAND("rib", "announce");
+
+ static void
+ validateRequestImpl(const RibAnnounceParameters& parameters);
+
+ static void
+ validateResponseImpl(const ControlParameters& parameters);
+};
+
} // namespace ndn::nfd
#endif // NDN_CXX_MGMT_NFD_CONTROL_COMMAND_HPP
diff --git a/tests/unit/mgmt/dispatcher.t.cpp b/tests/unit/mgmt/dispatcher.t.cpp
index 8caedc2..6379003 100644
--- a/tests/unit/mgmt/dispatcher.t.cpp
+++ b/tests/unit/mgmt/dispatcher.t.cpp
@@ -232,6 +232,7 @@
dispatcher.addControlCommand<nfd::StrategyChoiceUnsetCommand>(makeAcceptAllAuthorization(), handler);
dispatcher.addControlCommand<nfd::RibRegisterCommand>(makeAcceptAllAuthorization(), handler);
dispatcher.addControlCommand<nfd::RibUnregisterCommand>(makeAcceptAllAuthorization(), handler);
+ dispatcher.addControlCommand<nfd::RibAnnounceCommand>(makeAcceptAllAuthorization(), handler);
BOOST_CHECK_THROW(dispatcher.addControlCommand<nfd::CsConfigCommand>(makeAcceptAllAuthorization(),
[] (auto&&...) {}),
diff --git a/tests/unit/mgmt/nfd/control-command.t.cpp b/tests/unit/mgmt/nfd/control-command.t.cpp
index dc8cff0..8aa7f50 100644
--- a/tests/unit/mgmt/nfd/control-command.t.cpp
+++ b/tests/unit/mgmt/nfd/control-command.t.cpp
@@ -22,6 +22,7 @@
#include "ndn-cxx/mgmt/nfd/control-command.hpp"
#include "tests/boost-test.hpp"
+#include "tests/key-chain-fixture.hpp"
namespace ndn::tests {
@@ -492,6 +493,51 @@
BOOST_CHECK_THROW(Command::validateResponse(p2), ArgumentError);
}
+BOOST_FIXTURE_TEST_CASE(RibAnnounce, KeyChainFixture)
+{
+ using Command = RibAnnounceCommand;
+
+ // Good request
+ PrefixAnnouncement pa1;
+ pa1.setAnnouncedName("ndn:/");
+ pa1.setExpiration(1_min);
+ pa1.toData(m_keyChain);
+ RibAnnounceParameters p1;
+ p1.setPrefixAnnouncement(pa1);
+ BOOST_CHECK_NO_THROW(Command::validateRequest(p1));
+ Name n1 = Command::createRequest("/PREFIX", p1).getName();
+ BOOST_CHECK(Name("ndn:/PREFIX/rib/announce").isPrefixOf(n1));
+
+ // Good response
+ ControlParameters p2;
+ p2.setName("ndn:/")
+ .setFaceId(22)
+ .setOrigin(ndn::nfd::ROUTE_ORIGIN_PREFIXANN)
+ .setCost(2048)
+ .setFlags(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT)
+ .setExpirationPeriod(1_min);
+ BOOST_CHECK_NO_THROW(Command::validateResponse(p2));
+
+ // Bad request (PrefixAnnouncement must be signed)
+ PrefixAnnouncement pa2;
+ pa2.setAnnouncedName("ndn:/");
+ pa2.setExpiration(1_min);
+ RibAnnounceParameters p3;
+ BOOST_CHECK_THROW(Command::validateRequest(p3), ArgumentError);
+ p3.setPrefixAnnouncement(pa2);
+ BOOST_CHECK_THROW(Command::validateRequest(p3), ArgumentError);
+
+ // Bad response (FaceId must be valid)
+ ControlParameters p4;
+ p4.setName("ndn:/")
+ .setFaceId(0)
+ .setOrigin(ndn::nfd::ROUTE_ORIGIN_PREFIXANN)
+ .setCost(2048)
+ .setFlags(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT)
+ .setExpirationPeriod(1_min);
+ BOOST_CHECK_THROW(Command::validateResponse(p4), ArgumentError);
+}
+
BOOST_AUTO_TEST_SUITE_END() // TestControlCommand
BOOST_AUTO_TEST_SUITE_END() // Nfd
BOOST_AUTO_TEST_SUITE_END() // Mgmt
diff --git a/tests/unit/mgmt/nfd/controller.t.cpp b/tests/unit/mgmt/nfd/controller.t.cpp
index 8be9d23..9fad4ec 100644
--- a/tests/unit/mgmt/nfd/controller.t.cpp
+++ b/tests/unit/mgmt/nfd/controller.t.cpp
@@ -140,11 +140,15 @@
BOOST_AUTO_TEST_CASE(InvalidRequest)
{
- ControlParameters parameters;
- parameters.setName("ndn:/should-not-have-this-field");
+ ControlParameters p1;
+ p1.setName("/should-not-have-this-field");
// Uri is missing
+ BOOST_CHECK_THROW(controller.start<FaceCreateCommand>(p1, succeedCallback, commandFailCallback),
+ ArgumentError);
- BOOST_CHECK_THROW(controller.start<FaceCreateCommand>(parameters, succeedCallback, commandFailCallback),
+ RibAnnounceParameters p2;
+ // PrefixAnnouncement not signed
+ BOOST_CHECK_THROW(controller.start<RibAnnounceCommand>(p2, succeedCallback, commandFailCallback),
ArgumentError);
}