blob: ebc794ae86553d5fc605d0a494c3357aea498ae6 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2024, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mgmt/face-manager.hpp"
#include "face/generic-link-service.hpp"
#include "face-manager-command-fixture.hpp"
#include "tests/daemon/face/dummy-transport.hpp"
#include <ndn-cxx/lp/tags.hpp>
#include <boost/logic/tribool.hpp>
#include <boost/mp11/list.hpp>
#include <thread>
namespace nfd::tests {
BOOST_AUTO_TEST_SUITE(Mgmt)
BOOST_AUTO_TEST_SUITE(TestFaceManager)
class FaceManagerUpdateFixture : public FaceManagerCommandFixture
{
public:
~FaceManagerUpdateFixture() override
{
destroyFace();
}
void
createFace(const std::string& uri = "tcp4://127.0.0.1:26363",
ndn::nfd::FacePersistency persistency = ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
std::optional<time::nanoseconds> baseCongestionMarkingInterval = std::nullopt,
std::optional<uint64_t> defaultCongestionThreshold = std::nullopt,
bool enableLocalFields = false,
bool enableReliability = false,
boost::logic::tribool enableCongestionMarking = boost::logic::indeterminate)
{
ControlParameters params;
params.setUri(uri);
params.setFacePersistency(persistency);
if (baseCongestionMarkingInterval) {
params.setBaseCongestionMarkingInterval(*baseCongestionMarkingInterval);
}
if (defaultCongestionThreshold) {
params.setDefaultCongestionThreshold(*defaultCongestionThreshold);
}
params.setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED, enableLocalFields);
params.setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, enableReliability);
if (!boost::logic::indeterminate(enableCongestionMarking)) {
params.setFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED, bool(enableCongestionMarking));
}
createFace(params);
}
void
createFace(const ControlParameters& createParams, bool isForOnDemandFace = false)
{
Interest req = makeControlCommandRequest("/localhost/nfd/faces/create", createParams);
// if this creation if for on-demand face then create it on node2
FaceManagerCommandNode& target = isForOnDemandFace ? this->node2 : this->node1;
bool hasCallbackFired = false;
signal::ScopedConnection connection = target.face.onSendData.connect(
[&, req, isForOnDemandFace, this] (const Data& response) {
if (!req.getName().isPrefixOf(response.getName())) {
return;
}
ControlResponse create(response.getContent().blockFromValue());
BOOST_REQUIRE_EQUAL(create.getCode(), 200);
BOOST_REQUIRE(create.getBody().hasWire());
ControlParameters faceParams(create.getBody());
BOOST_REQUIRE(faceParams.hasFaceId());
this->faceId = faceParams.getFaceId();
hasCallbackFired = true;
if (isForOnDemandFace) {
auto face = target.faceTable.get(static_cast<FaceId>(this->faceId));
// to force creation of on-demand face
face->sendInterest(*makeInterest("/hello/world"));
}
});
target.face.receive(req);
advanceClocks(1_ms, 5);
if (isForOnDemandFace) {
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // allow wallclock time for socket IO
advanceClocks(1_ms, 5); // let node1 accept Interest and create on-demand face
}
BOOST_REQUIRE(hasCallbackFired);
}
void
updateFace(const ControlParameters& requestParams,
bool isSelfUpdating,
const std::function<void(const ControlResponse& resp)>& checkResp)
{
Interest req = makeControlCommandRequest("/localhost/nfd/faces/update", requestParams);
if (isSelfUpdating) {
// Attach IncomingFaceIdTag to interest
req.setTag(make_shared<lp::IncomingFaceIdTag>(faceId));
}
bool hasCallbackFired = false;
signal::ScopedConnection connection = this->node1.face.onSendData.connect(
[req, &hasCallbackFired, &checkResp] (const Data& response) {
if (!req.getName().isPrefixOf(response.getName())) {
return;
}
ControlResponse actual(response.getContent().blockFromValue());
checkResp(actual);
hasCallbackFired = true;
});
this->node1.face.receive(req);
advanceClocks(1_ms, 5);
BOOST_REQUIRE(hasCallbackFired);
}
private:
void
destroyFace()
{
if (faceId == 0) {
return;
}
ControlParameters params;
params.setFaceId(faceId);
Interest req = makeControlCommandRequest("/localhost/nfd/faces/destroy", params);
bool hasCallbackFired = false;
signal::ScopedConnection connection = this->node1.face.onSendData.connect(
[this, req, &hasCallbackFired] (const Data& response) {
if (!req.getName().isPrefixOf(response.getName())) {
return;
}
ControlResponse destroy(response.getContent().blockFromValue());
BOOST_CHECK_EQUAL(destroy.getCode(), 200);
faceId = 0;
hasCallbackFired = true;
});
this->node1.face.receive(req);
advanceClocks(1_ms, 5);
BOOST_REQUIRE(hasCallbackFired);
}
protected:
FaceId faceId = 0;
};
BOOST_FIXTURE_TEST_SUITE(UpdateFace, FaceManagerUpdateFixture)
BOOST_AUTO_TEST_CASE(FaceDoesNotExist)
{
ControlParameters requestParams;
requestParams.setFaceId(65535);
updateFace(requestParams, false, [] (const ControlResponse& actual) {
ControlResponse expected(404, "Specified face does not exist");
BOOST_CHECK_EQUAL(actual.getCode(), expected.getCode());
BOOST_TEST_MESSAGE(actual.getText());
});
}
using UpdatePersistencyTests = boost::mp11::mp_list<
boost::mp11::mp_list<DummyTransportBase<true>, CommandSuccess>,
boost::mp11::mp_list<DummyTransportBase<false>, CommandFailure<409>>
>;
BOOST_FIXTURE_TEST_CASE_TEMPLATE(UpdatePersistency, T, UpdatePersistencyTests, FaceManagerUpdateFixture)
{
using TransportType = boost::mp11::mp_first<T>;
using ResultType = boost::mp11::mp_second<T>;
auto face = make_shared<face::Face>(make_unique<face::GenericLinkService>(),
make_unique<TransportType>());
this->node1.faceTable.add(face);
auto parameters = ControlParameters()
.setFaceId(face->getId())
.setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERMANENT);
updateFace(parameters, false, [] (const ControlResponse& actual) {
BOOST_TEST_MESSAGE(actual.getText());
BOOST_CHECK_EQUAL(actual.getCode(), ResultType::getExpected().getCode());
// the response for either 200 or 409 will have a content body
BOOST_REQUIRE(actual.getBody().hasWire());
ControlParameters resp;
resp.wireDecode(actual.getBody());
BOOST_CHECK_EQUAL(resp.getFacePersistency(), ndn::nfd::FACE_PERSISTENCY_PERMANENT);
});
}
BOOST_AUTO_TEST_CASE(UpdateMtu)
{
createFace("udp4://127.0.0.1:26363");
ControlParameters validParams;
validParams.setFaceId(faceId);
validParams.setMtu(4000);
ControlParameters mtuTooLow;
mtuTooLow.setFaceId(faceId);
mtuTooLow.setMtu(63);
updateFace(validParams, false, [] (const ControlResponse& actual) {
BOOST_CHECK_EQUAL(actual.getCode(), 200);
BOOST_TEST_MESSAGE(actual.getText());
if (actual.getBody().hasWire()) {
ControlParameters actualParams(actual.getBody());
BOOST_CHECK(actualParams.hasFaceId());
BOOST_REQUIRE(actualParams.hasMtu());
// Check for changed MTU
BOOST_CHECK_EQUAL(actualParams.getMtu(), 4000);
}
else {
BOOST_ERROR("Valid: Response does not contain ControlParameters");
}
});
updateFace(mtuTooLow, false, [] (const ControlResponse& actual) {
BOOST_CHECK_EQUAL(actual.getCode(), 409);
BOOST_TEST_MESSAGE(actual.getText());
if (actual.getBody().hasWire()) {
ControlParameters actualParams(actual.getBody());
BOOST_CHECK(!actualParams.hasFaceId());
BOOST_REQUIRE(actualParams.hasMtu());
// Check for returned invalid parameter
BOOST_CHECK_EQUAL(actualParams.getMtu(), 63);
}
else {
BOOST_ERROR("Too low: Response does not contain ControlParameters");
}
});
}
BOOST_AUTO_TEST_CASE(UpdateMtuUnsupportedFace)
{
createFace("tcp4://127.0.0.1:26363");
ControlParameters updateParams;
updateParams.setFaceId(faceId);
updateParams.setMtu(4000);
updateFace(updateParams, false, [] (const ControlResponse& actual) {
BOOST_CHECK_EQUAL(actual.getCode(), 409);
BOOST_TEST_MESSAGE(actual.getText());
if (actual.getBody().hasWire()) {
ControlParameters actualParams(actual.getBody());
BOOST_CHECK(!actualParams.hasFaceId());
BOOST_REQUIRE(actualParams.hasMtu());
// Check for returned invalid parameter
BOOST_CHECK_EQUAL(actualParams.getMtu(), 4000);
}
else {
BOOST_ERROR("Response does not contain ControlParameters");
}
});
}
class TcpLocalFieldsEnable
{
public:
static std::string
getUri()
{
return "tcp4://127.0.0.1:26363";
}
static constexpr ndn::nfd::FacePersistency
getPersistency()
{
return ndn::nfd::FACE_PERSISTENCY_PERSISTENT;
}
static constexpr bool
getInitLocalFieldsEnabled()
{
return false;
}
static constexpr bool
getLocalFieldsEnabled()
{
return true;
}
static constexpr bool
getLocalFieldsEnabledMask()
{
return true;
}
static constexpr bool
shouldHaveWire()
{
return false;
}
};
class TcpLocalFieldsDisable
{
public:
static std::string
getUri()
{
return "tcp4://127.0.0.1:26363";
}
static constexpr ndn::nfd::FacePersistency
getPersistency()
{
return ndn::nfd::FACE_PERSISTENCY_PERSISTENT;
}
static constexpr bool
getInitLocalFieldsEnabled()
{
return true;
}
static constexpr bool
getLocalFieldsEnabled()
{
return false;
}
static constexpr bool
getLocalFieldsEnabledMask()
{
return true;
}
static constexpr bool
shouldHaveWire()
{
return false;
}
};
// UDP faces are non-local by definition
class UdpLocalFieldsEnable
{
public:
static std::string
getUri()
{
return "udp4://127.0.0.1:26363";
}
static constexpr ndn::nfd::FacePersistency
getPersistency()
{
return ndn::nfd::FACE_PERSISTENCY_PERSISTENT;
}
static constexpr bool
getInitLocalFieldsEnabled()
{
return false;
}
static constexpr bool
getLocalFieldsEnabled()
{
return true;
}
static constexpr bool
getLocalFieldsEnabledMask()
{
return true;
}
static constexpr bool
shouldHaveWire()
{
return true;
}
};
// UDP faces are non-local by definition
// In this test case, attempt to disable local fields on face with local fields already disabled
class UdpLocalFieldsDisable
{
public:
static std::string
getUri()
{
return "udp4://127.0.0.1:26363";
}
static constexpr ndn::nfd::FacePersistency
getPersistency()
{
return ndn::nfd::FACE_PERSISTENCY_PERSISTENT;
}
static constexpr bool
getInitLocalFieldsEnabled()
{
return false;
}
static constexpr bool
getLocalFieldsEnabled()
{
return false;
}
static constexpr bool
getLocalFieldsEnabledMask()
{
return true;
}
static constexpr bool
shouldHaveWire()
{
return false;
}
};
// In this test case, set Flags to enable local fields on non-local face, but exclude local fields
// from Mask. This test case will pass as no action is taken due to the missing Mask bit.
class UdpLocalFieldsEnableNoMaskBit
{
public:
static std::string
getUri()
{
return "udp4://127.0.0.1:26363";
}
static constexpr ndn::nfd::FacePersistency
getPersistency()
{
return ndn::nfd::FACE_PERSISTENCY_PERSISTENT;
}
static constexpr bool
getInitLocalFieldsEnabled()
{
return false;
}
static constexpr bool
getLocalFieldsEnabled()
{
return true;
}
static constexpr bool
getLocalFieldsEnabledMask()
{
return false;
}
static constexpr bool
shouldHaveWire()
{
return false;
}
};
using LocalFieldFaces = boost::mp11::mp_list<
boost::mp11::mp_list<TcpLocalFieldsEnable, CommandSuccess>,
boost::mp11::mp_list<TcpLocalFieldsDisable, CommandSuccess>,
boost::mp11::mp_list<UdpLocalFieldsEnable, CommandFailure<409>>,
boost::mp11::mp_list<UdpLocalFieldsDisable, CommandSuccess>,
boost::mp11::mp_list<UdpLocalFieldsEnableNoMaskBit, CommandSuccess>
>;
BOOST_AUTO_TEST_CASE_TEMPLATE(UpdateLocalFields, T, LocalFieldFaces)
{
using TestType = boost::mp11::mp_first<T>;
using ResultType = boost::mp11::mp_second<T>;
createFace(TestType::getUri(), TestType::getPersistency(), {}, {},
TestType::getInitLocalFieldsEnabled());
ControlParameters requestParams;
requestParams.setFaceId(faceId);
requestParams.setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED, TestType::getLocalFieldsEnabled());
if (!TestType::getLocalFieldsEnabledMask()) {
requestParams.unsetFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED);
}
updateFace(requestParams, false, [] (const ControlResponse& actual) {
ControlResponse expected(ResultType::getExpected());
BOOST_TEST_MESSAGE(actual.getText());
BOOST_CHECK_EQUAL(actual.getCode(), expected.getCode());
if (TestType::shouldHaveWire() && actual.getBody().hasWire()) {
ControlParameters actualParams(actual.getBody());
BOOST_CHECK(!actualParams.hasFacePersistency());
BOOST_CHECK(actualParams.hasFlags());
BOOST_CHECK(actualParams.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED));
BOOST_CHECK(actualParams.hasFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED));
}
});
}
BOOST_AUTO_TEST_CASE(UpdateLocalFieldsEnableDisable)
{
createFace();
ControlParameters enableParams;
enableParams.setFaceId(faceId);
enableParams.setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED, true);
ControlParameters disableParams;
disableParams.setFaceId(faceId);
disableParams.setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED, false);
updateFace(enableParams, false, [] (const ControlResponse& actual) {
ControlResponse expected(200, "OK");
BOOST_CHECK_EQUAL(actual.getCode(), expected.getCode());
BOOST_TEST_MESSAGE(actual.getText());
if (actual.getBody().hasWire()) {
ControlParameters actualParams(actual.getBody());
BOOST_CHECK(actualParams.hasFaceId());
BOOST_CHECK(actualParams.hasFacePersistency());
BOOST_REQUIRE(actualParams.hasFlags());
// Check if flags indicate local fields enabled
BOOST_CHECK(actualParams.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED));
}
else {
BOOST_ERROR("Enable: Response does not contain ControlParameters");
}
});
updateFace(disableParams, false, [] (const ControlResponse& actual) {
ControlResponse expected(200, "OK");
BOOST_CHECK_EQUAL(actual.getCode(), expected.getCode());
BOOST_TEST_MESSAGE(actual.getText());
if (actual.getBody().hasWire()) {
ControlParameters actualParams(actual.getBody());
BOOST_CHECK(actualParams.hasFaceId());
BOOST_CHECK(actualParams.hasFacePersistency());
BOOST_REQUIRE(actualParams.hasFlags());
// Check if flags indicate local fields disabled
BOOST_CHECK(!actualParams.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED));
}
else {
BOOST_ERROR("Disable: Response does not contain ControlParameters");
}
});
}
BOOST_AUTO_TEST_CASE(UpdateReliabilityEnableDisable)
{
createFace("udp4://127.0.0.1:26363");
ControlParameters enableParams;
enableParams.setFaceId(faceId);
enableParams.setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, true);
ControlParameters disableParams;
disableParams.setFaceId(faceId);
disableParams.setFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED, false);
updateFace(enableParams, false, [] (const ControlResponse& actual) {
ControlResponse expected(200, "OK");
BOOST_CHECK_EQUAL(actual.getCode(), expected.getCode());
BOOST_TEST_MESSAGE(actual.getText());
if (actual.getBody().hasWire()) {
ControlParameters actualParams(actual.getBody());
BOOST_CHECK(actualParams.hasFaceId());
BOOST_CHECK(actualParams.hasFacePersistency());
BOOST_REQUIRE(actualParams.hasFlags());
// Check if flags indicate reliability enabled
BOOST_CHECK(actualParams.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED));
}
else {
BOOST_ERROR("Enable: Response does not contain ControlParameters");
}
});
updateFace(disableParams, false, [] (const ControlResponse& actual) {
ControlResponse expected(200, "OK");
BOOST_CHECK_EQUAL(actual.getCode(), expected.getCode());
BOOST_TEST_MESSAGE(actual.getText());
if (actual.getBody().hasWire()) {
ControlParameters actualParams(actual.getBody());
BOOST_CHECK(actualParams.hasFaceId());
BOOST_CHECK(actualParams.hasFacePersistency());
BOOST_REQUIRE(actualParams.hasFlags());
// Check if flags indicate reliability disabled
BOOST_CHECK(!actualParams.getFlagBit(ndn::nfd::BIT_LP_RELIABILITY_ENABLED));
}
else {
BOOST_ERROR("Disable: Response does not contain ControlParameters");
}
});
}
BOOST_AUTO_TEST_CASE(UpdateCongestionMarkingEnableDisable)
{
createFace("udp4://127.0.0.1:26363");
ControlParameters enableParams;
enableParams.setFaceId(faceId);
enableParams.setBaseCongestionMarkingInterval(50_ms);
enableParams.setDefaultCongestionThreshold(10000);
enableParams.setFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED, true);
ControlParameters disableParams;
disableParams.setFaceId(faceId);
disableParams.setBaseCongestionMarkingInterval(70_ms);
disableParams.setDefaultCongestionThreshold(5000);
disableParams.setFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED, false);
updateFace(enableParams, false, [] (const ControlResponse& actual) {
ControlResponse expected(200, "OK");
BOOST_CHECK_EQUAL(actual.getCode(), expected.getCode());
BOOST_TEST_MESSAGE(actual.getText());
if (actual.getBody().hasWire()) {
ControlParameters actualParams(actual.getBody());
BOOST_CHECK(actualParams.hasFaceId());
BOOST_CHECK(actualParams.hasFacePersistency());
// Check that congestion marking parameters changed
BOOST_REQUIRE(actualParams.hasBaseCongestionMarkingInterval());
BOOST_CHECK_EQUAL(actualParams.getBaseCongestionMarkingInterval(), 50_ms);
BOOST_REQUIRE(actualParams.hasDefaultCongestionThreshold());
BOOST_CHECK_EQUAL(actualParams.getDefaultCongestionThreshold(), 10000);
BOOST_REQUIRE(actualParams.hasFlags());
// Check if flags indicate congestion marking enabled
BOOST_CHECK(actualParams.getFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED));
}
else {
BOOST_ERROR("Enable: Response does not contain ControlParameters");
}
});
updateFace(disableParams, false, [] (const ControlResponse& actual) {
ControlResponse expected(200, "OK");
BOOST_CHECK_EQUAL(actual.getCode(), expected.getCode());
BOOST_TEST_MESSAGE(actual.getText());
if (actual.getBody().hasWire()) {
ControlParameters actualParams(actual.getBody());
BOOST_CHECK(actualParams.hasFaceId());
BOOST_CHECK(actualParams.hasFacePersistency());
// Check that congestion marking parameters changed, even though feature disabled
BOOST_REQUIRE(actualParams.hasBaseCongestionMarkingInterval());
BOOST_CHECK_EQUAL(actualParams.getBaseCongestionMarkingInterval(), 70_ms);
BOOST_REQUIRE(actualParams.hasDefaultCongestionThreshold());
BOOST_CHECK_EQUAL(actualParams.getDefaultCongestionThreshold(), 5000);
BOOST_REQUIRE(actualParams.hasFlags());
// Check if flags indicate marking disabled
BOOST_CHECK(!actualParams.getFlagBit(ndn::nfd::BIT_CONGESTION_MARKING_ENABLED));
}
else {
BOOST_ERROR("Disable: Response does not contain ControlParameters");
}
});
}
BOOST_AUTO_TEST_CASE(SelfUpdating)
{
createFace();
// Send a command that does nothing (will return 200) and does not contain a FaceId
ControlParameters sentParams;
updateFace(sentParams, true, [] (const ControlResponse& actual) {
ControlResponse expected(200, "OK");
BOOST_REQUIRE_EQUAL(actual.getCode(), expected.getCode());
BOOST_TEST_MESSAGE(actual.getText());
});
}
BOOST_AUTO_TEST_SUITE_END() // UpdateFace
BOOST_AUTO_TEST_SUITE_END() // TestFaceManager
BOOST_AUTO_TEST_SUITE_END() // Mgmt
} // namespace nfd::tests