mgmt: handle rib/announce command
This commit adds logic to NFD so it can handle the new Prefix
Announcement protocol, which are commands sent to rib/announce
with Prefix Announcement object in the Application Parameters.
Refs: #4650
Change-Id: I2a306eb2c3eeb638cc789329d998bfa278880ca6
diff --git a/daemon/mgmt/rib-manager.cpp b/daemon/mgmt/rib-manager.cpp
index 6cc614a..ddc53cf 100644
--- a/daemon/mgmt/rib-manager.cpp
+++ b/daemon/mgmt/rib-manager.cpp
@@ -67,6 +67,9 @@
registerCommandHandler<ndn::nfd::RibUnregisterCommand>([this] (auto&&, auto&&... args) {
unregisterEntry(std::forward<decltype(args)>(args)...);
});
+ registerCommandHandler<ndn::nfd::RibAnnounceCommand>([this] (auto&&, auto&&... args) {
+ announceEntry(std::forward<decltype(args)>(args)...);
+ });
registerStatusDatasetHandler("list", [this] (auto&&, auto&&, auto&&... args) {
listEntries(std::forward<decltype(args)>(args)...);
});
@@ -266,6 +269,44 @@
}
void
+RibManager::announceEntry(const Interest& interest, const ndn::nfd::RibAnnounceParameters& parameters,
+ const CommandContinuation& done)
+{
+ const auto& announcement = parameters.getPrefixAnnouncement();
+ if (announcement.getAnnouncedName().size() > Fib::getMaxDepth()) {
+ done(ControlResponse(414, "Route prefix cannot exceed " + std::to_string(Fib::getMaxDepth()) +
+ " components"));
+ return;
+ }
+
+ // Prepare parameters for response
+ ControlParameters responseParams;
+ responseParams.setFaceId(0);
+ setFaceForSelfRegistration(interest, responseParams);
+
+ Route route(announcement, responseParams.getFaceId());
+
+ responseParams
+ .setName(announcement.getAnnouncedName())
+ .setOrigin(route.origin)
+ .setCost(route.cost)
+ .setFlags(route.flags)
+ .setExpirationPeriod(time::duration_cast<time::milliseconds>(route.annExpires - time::steady_clock::now()));
+
+ BOOST_ASSERT(announcement.getData());
+ m_paValidator.validate(*announcement.getData(),
+ [=, name = announcement.getAnnouncedName(), route = std::move(route)] (const Data&) {
+ // Respond since command is valid and authorized
+ done(ControlResponse(200, "Success").setBody(responseParams.wireEncode()));
+ beginAddRoute(name, std::move(route), std::nullopt, [] (RibUpdateResult) {});
+ },
+ [=] (const Data&, ndn::security::ValidationError err) {
+ done(ControlResponse(403, "Validation error: " + err.getInfo()));
+ }
+ );
+}
+
+void
RibManager::listEntries(ndn::mgmt::StatusDatasetContext& context)
{
auto now = time::steady_clock::now();
@@ -311,7 +352,8 @@
const ndn::mgmt::AcceptContinuation& accept,
const ndn::mgmt::RejectContinuation& reject) {
BOOST_ASSERT(params != nullptr);
- BOOST_ASSERT(typeid(*params) == typeid(ndn::nfd::ControlParameters));
+ BOOST_ASSERT(typeid(*params) == typeid(ndn::nfd::ControlParameters) ||
+ typeid(*params) == typeid(ndn::nfd::RibAnnounceParameters));
BOOST_ASSERT(prefix == LOCALHOST_TOP_PREFIX || prefix == LOCALHOP_TOP_PREFIX);
auto& validator = prefix == LOCALHOST_TOP_PREFIX ? m_localhostValidator : m_localhopValidator;
diff --git a/daemon/mgmt/rib-manager.hpp b/daemon/mgmt/rib-manager.hpp
index a7edc22..3b348a1 100644
--- a/daemon/mgmt/rib-manager.hpp
+++ b/daemon/mgmt/rib-manager.hpp
@@ -212,6 +212,13 @@
const CommandContinuation& done);
/**
+ * \brief Serve `rib/announce` command.
+ */
+ void
+ announceEntry(const Interest& interest, const ndn::nfd::RibAnnounceParameters& parameters,
+ const CommandContinuation& done);
+
+ /**
* \brief Serve `rib/list` dataset.
*/
void
diff --git a/daemon/rib/route.cpp b/daemon/rib/route.cpp
index e7c825f..5011290 100644
--- a/daemon/rib/route.cpp
+++ b/daemon/rib/route.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2014-2023, Regents of the University of California,
+ * Copyright (c) 2014-2025, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -29,8 +29,6 @@
namespace nfd::rib {
-constexpr uint64_t PA_ROUTE_COST = 2048; // cost of route created by prefix announcement
-
Route::Route() = default;
static time::steady_clock::time_point
diff --git a/daemon/rib/route.hpp b/daemon/rib/route.hpp
index 2c275df..43f520d 100644
--- a/daemon/rib/route.hpp
+++ b/daemon/rib/route.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2014-2024, Regents of the University of California,
+ * Copyright (c) 2014-2025, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -90,6 +90,9 @@
}
public:
+ /// Cost of route created by prefix announcement.
+ static constexpr uint64_t PA_ROUTE_COST = 2048;
+
uint64_t faceId = 0;
ndn::nfd::RouteOrigin origin = ndn::nfd::ROUTE_ORIGIN_APP;
uint64_t cost = 0;
diff --git a/tests/daemon/mgmt/manager-common-fixture.cpp b/tests/daemon/mgmt/manager-common-fixture.cpp
index 3b98dd2..e918cdb 100644
--- a/tests/daemon/mgmt/manager-common-fixture.cpp
+++ b/tests/daemon/mgmt/manager-common-fixture.cpp
@@ -52,6 +52,20 @@
NDN_CXX_UNREACHABLE;
}
+Interest
+InterestSignerFixture::makeControlCommandRequest(Name commandName,
+ const ndn::PrefixAnnouncement& prefixAnnouncement,
+ const Name& identity)
+{
+ const Block& paBlock = prefixAnnouncement.getData().value().wireEncode();
+
+ Interest interest(commandName);
+ interest.setApplicationParameters(paBlock);
+ m_signer.makeSignedInterest(interest, ndn::security::signingByIdentity(identity));
+
+ return interest;
+}
+
void
ManagerCommonFixture::setTopPrefix()
{
diff --git a/tests/daemon/mgmt/manager-common-fixture.hpp b/tests/daemon/mgmt/manager-common-fixture.hpp
index c8f5498..22d9120 100644
--- a/tests/daemon/mgmt/manager-common-fixture.hpp
+++ b/tests/daemon/mgmt/manager-common-fixture.hpp
@@ -59,6 +59,19 @@
ndn::security::SignedInterestFormat format = ndn::security::SignedInterestFormat::V03,
const Name& identity = DEFAULT_COMMAND_SIGNER_IDENTITY);
+ /**
+ * \brief Create a ControlCommand request for a Prefix Announcement.
+ * \param commandName Command name including prefix, such as `/localhost/nfd/rib/announce`
+ * \param prefixAnnouncement Prefix Announcement object
+ * \param identity Signing identity
+ *
+ * Per specification, Prefix Announcements use Signed Interest v0.3 only.
+ */
+ Interest
+ makeControlCommandRequest(Name commandName,
+ const ndn::PrefixAnnouncement& prefixAnnouncement,
+ const Name& identity = DEFAULT_COMMAND_SIGNER_IDENTITY);
+
protected:
static inline const Name DEFAULT_COMMAND_SIGNER_IDENTITY{"/InterestSignerFixture-identity"};
diff --git a/tests/daemon/mgmt/rib-manager.t.cpp b/tests/daemon/mgmt/rib-manager.t.cpp
index 43c69df..679f40c 100644
--- a/tests/daemon/mgmt/rib-manager.t.cpp
+++ b/tests/daemon/mgmt/rib-manager.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2014-2024, Regents of the University of California,
+ * Copyright (c) 2014-2025, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -117,6 +117,14 @@
return makeSection(config);
}
+static ConfigSection
+getPaValidatorConfigSection()
+{
+ ConfigSection section;
+ section.put("trust-anchor.type", "any");
+ return section;
+}
+
class RibManagerFixture : public ManagerCommonFixture
{
public:
@@ -151,6 +159,8 @@
m_manager.disableLocalhop();
}
+ m_manager.applyPaConfig(getPaValidatorConfigSection(), "testPa");
+
registerWithNfd();
if (shouldClearRib) {
@@ -442,6 +452,7 @@
}
auto params = makeRegisterParameters(prefix, 2899);
auto command = makeControlCommandRequest("/localhost/nfd/rib/register", params);
+
receiveInterest(command);
BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
@@ -455,6 +466,107 @@
BOOST_AUTO_TEST_SUITE_END() // RegisterUnregister
+BOOST_FIXTURE_TEST_SUITE(PrefixAnnounce, LocalhostAuthorizedRibManagerFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ const uint64_t announceFaceId = 1234;
+
+ ndn::PrefixAnnouncement pa = signPrefixAnn(makePrefixAnn("/test-prefix-announce", 10_s), m_keyChain);
+ auto commandAnnounce = makeControlCommandRequest("/localhost/nfd/rib/announce", pa);
+ commandAnnounce.setTag(make_shared<lp::IncomingFaceIdTag>(announceFaceId));
+
+ auto paramsUnregister = makeUnregisterParameters("/test-prefix-announce");
+ paramsUnregister.setOrigin(ndn::nfd::ROUTE_ORIGIN_PREFIXANN);
+ BOOST_CHECK_EQUAL(paramsUnregister.getFaceId(), 0);
+ auto commandUnregister = makeControlCommandRequest("/localhost/nfd/rib/unregister", paramsUnregister);
+ commandUnregister.setTag(make_shared<lp::IncomingFaceIdTag>(announceFaceId)); // same incoming face
+
+ receiveInterest(commandAnnounce);
+ receiveInterest(commandUnregister);
+
+ ControlParameters paramsAnnounceResponse;
+ paramsAnnounceResponse.setName("/test-prefix-announce")
+ .setFaceId(announceFaceId)
+ .setOrigin(ndn::nfd::ROUTE_ORIGIN_PREFIXANN)
+ .setCost(rib::Route::PA_ROUTE_COST)
+ .setFlags(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT)
+ .setExpirationPeriod(10_s);
+ paramsUnregister.setFaceId(announceFaceId);
+
+ BOOST_REQUIRE_EQUAL(m_responses.size(), 2);
+ BOOST_CHECK_EQUAL(checkResponse(0, commandAnnounce.getName(), makeResponse(200, "Success", paramsAnnounceResponse)),
+ CheckResponseResult::OK);
+ BOOST_CHECK_EQUAL(checkResponse(1, commandUnregister.getName(), makeResponse(200, "Success", paramsUnregister)),
+ CheckResponseResult::OK);
+
+ BOOST_REQUIRE_EQUAL(m_fibUpdater.updates.size(), 2);
+ BOOST_CHECK_EQUAL(m_fibUpdater.updates.front(),
+ rib::FibUpdate::createAddUpdate("/test-prefix-announce", announceFaceId, rib::Route::PA_ROUTE_COST));
+ BOOST_CHECK_EQUAL(m_fibUpdater.updates.back(),
+ rib::FibUpdate::createRemoveUpdate("/test-prefix-announce", announceFaceId));
+}
+
+BOOST_AUTO_TEST_CASE(UnregisterFromDifferentFace)
+{
+ const uint64_t announceFaceId = 1234;
+
+ ndn::PrefixAnnouncement pa = signPrefixAnn(makePrefixAnn("/test-prefix-announce", 10_s), m_keyChain);
+ auto commandAnnounce = makeControlCommandRequest("/localhost/nfd/rib/announce", pa);
+ commandAnnounce.setTag(make_shared<lp::IncomingFaceIdTag>(announceFaceId));
+
+ auto paramsUnregister = makeUnregisterParameters("/test-prefix-announce", announceFaceId);
+ paramsUnregister.setOrigin(ndn::nfd::ROUTE_ORIGIN_PREFIXANN);
+ auto commandUnregister = makeControlCommandRequest("/localhost/nfd/rib/unregister", paramsUnregister);
+ commandUnregister.setTag(make_shared<lp::IncomingFaceIdTag>(999)); // unregister from different face
+
+ receiveInterest(commandAnnounce);
+ receiveInterest(commandUnregister);
+
+ ControlParameters paramsAnnounceResponse;
+ paramsAnnounceResponse.setName("/test-prefix-announce")
+ .setFaceId(announceFaceId)
+ .setOrigin(ndn::nfd::ROUTE_ORIGIN_PREFIXANN)
+ .setCost(rib::Route::PA_ROUTE_COST)
+ .setFlags(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT)
+ .setExpirationPeriod(10_s);
+
+ BOOST_REQUIRE_EQUAL(m_responses.size(), 2);
+ BOOST_CHECK_EQUAL(checkResponse(0, commandAnnounce.getName(), makeResponse(200, "Success", paramsAnnounceResponse)),
+ CheckResponseResult::OK);
+ BOOST_CHECK_EQUAL(checkResponse(1, commandUnregister.getName(), makeResponse(200, "Success", paramsUnregister)),
+ CheckResponseResult::OK);
+
+ BOOST_REQUIRE_EQUAL(m_fibUpdater.updates.size(), 2);
+ BOOST_CHECK_EQUAL(m_fibUpdater.updates.front(),
+ rib::FibUpdate::createAddUpdate("/test-prefix-announce", announceFaceId, rib::Route::PA_ROUTE_COST));
+ BOOST_CHECK_EQUAL(m_fibUpdater.updates.back(),
+ rib::FibUpdate::createRemoveUpdate("/test-prefix-announce", announceFaceId));
+}
+
+BOOST_AUTO_TEST_CASE(NameTooLong)
+{
+ Name prefix;
+ while (prefix.size() <= Fib::getMaxDepth()) {
+ prefix.append("A");
+ }
+ ndn::PrefixAnnouncement pa = signPrefixAnn(makePrefixAnn(prefix, 10_s), m_keyChain);
+ auto command = makeControlCommandRequest("/localhost/nfd/rib/announce", pa);
+ command.setTag(make_shared<lp::IncomingFaceIdTag>(333));
+
+ receiveInterest(command);
+
+ BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+ BOOST_CHECK_EQUAL(checkResponse(0, command.getName(),
+ ControlResponse(414, "Route prefix cannot exceed " +
+ std::to_string(Fib::getMaxDepth()) + " components")),
+ CheckResponseResult::OK);
+
+ BOOST_CHECK_EQUAL(m_fibUpdater.updates.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // PrefixAnnounce
+
BOOST_FIXTURE_TEST_CASE(RibDataset, UnauthorizedRibManagerFixture)
{
uint64_t faceId = 0;