blob: e6748be968a95bc2a5b90cbe4e85af21a0958d0a [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (C) 2014 Named Data Networking Project
* See COPYING for copyright and distribution information.
*/
#include "mgmt/fib-manager.hpp"
#include "table/fib.hpp"
#include "table/fib-nexthop.hpp"
#include "face/face.hpp"
#include "mgmt/internal-face.hpp"
#include "tests/face/dummy-face.hpp"
#include "validation-common.hpp"
#include "tests/test-common.hpp"
namespace nfd {
namespace tests {
NFD_LOG_INIT("FibManagerTest");
class FibManagerFixture : protected BaseFixture
{
public:
virtual
~FibManagerFixture()
{
}
shared_ptr<Face>
getFace(FaceId id)
{
if (id > 0 && static_cast<size_t>(id) <= m_faces.size())
{
return m_faces[id - 1];
}
NFD_LOG_DEBUG("No face found returning NULL");
return shared_ptr<DummyFace>();
}
void
addFace(shared_ptr<Face> face)
{
m_faces.push_back(face);
}
void
validateControlResponseCommon(const Data& response,
const Name& expectedName,
uint32_t expectedCode,
const std::string& expectedText,
ControlResponse& control)
{
m_callbackFired = true;
Block controlRaw = response.getContent().blockFromValue();
control.wireDecode(controlRaw);
// NFD_LOG_DEBUG("received control response"
// << " Name: " << response.getName()
// << " code: " << control.getCode()
// << " text: " << control.getText());
BOOST_CHECK_EQUAL(response.getName(), expectedName);
BOOST_CHECK_EQUAL(control.getCode(), expectedCode);
BOOST_CHECK_EQUAL(control.getText(), expectedText);
}
void
validateControlResponse(const Data& response,
const Name& expectedName,
uint32_t expectedCode,
const std::string& expectedText)
{
ControlResponse control;
validateControlResponseCommon(response, expectedName,
expectedCode, expectedText, control);
if (!control.getBody().empty())
{
BOOST_FAIL("found unexpected control response body");
}
}
void
validateControlResponse(const Data& response,
const Name& expectedName,
uint32_t expectedCode,
const std::string& expectedText,
const Block& expectedBody)
{
ControlResponse control;
validateControlResponseCommon(response, expectedName,
expectedCode, expectedText, control);
BOOST_REQUIRE(!control.getBody().empty());
BOOST_REQUIRE(control.getBody().value_size() == expectedBody.value_size());
BOOST_CHECK(memcmp(control.getBody().value(), expectedBody.value(),
expectedBody.value_size()) == 0);
}
bool
didCallbackFire()
{
return m_callbackFired;
}
void
resetCallbackFired()
{
m_callbackFired = false;
}
shared_ptr<InternalFace>
getInternalFace()
{
return m_face;
}
FibManager&
getFibManager()
{
return m_manager;
}
Fib&
getFib()
{
return m_fib;
}
void
addInterestRule(const std::string& regex,
ndn::IdentityCertificate& certificate)
{
m_manager.addInterestRule(regex, certificate);
}
protected:
FibManagerFixture()
: m_face(make_shared<InternalFace>())
, m_nameTree(1024)
, m_fib(m_nameTree)
, m_manager(boost::ref(m_fib),
bind(&FibManagerFixture::getFace, this, _1),
m_face)
, m_callbackFired(false)
{
}
private:
shared_ptr<InternalFace> m_face;
NameTree m_nameTree;
Fib m_fib;
FibManager m_manager;
std::vector<shared_ptr<Face> > m_faces;
bool m_callbackFired;
};
template <typename T> class AuthorizedCommandFixture:
public CommandFixture<T>
{
public:
AuthorizedCommandFixture()
{
const std::string regex = "^<localhost><nfd><fib>";
T::addInterestRule(regex, *CommandFixture<T>::m_certificate);
}
virtual
~AuthorizedCommandFixture()
{
}
};
BOOST_FIXTURE_TEST_SUITE(MgmtFibManager, AuthorizedCommandFixture<FibManagerFixture>)
bool
foundNextHop(FaceId id, uint32_t cost, const fib::NextHop& next)
{
return id == next.getFace()->getId() && next.getCost() == cost;
}
bool
addedNextHopWithCost(const Fib& fib, const Name& prefix, size_t oldSize, uint32_t cost)
{
shared_ptr<fib::Entry> entry = fib.findExactMatch(prefix);
if (static_cast<bool>(entry))
{
const fib::NextHopList& hops = entry->getNextHops();
return hops.size() == oldSize + 1 &&
std::find_if(hops.begin(), hops.end(), bind(&foundNextHop, -1, cost, _1)) != hops.end();
}
return false;
}
bool
foundNextHopWithFace(FaceId id, uint32_t cost,
shared_ptr<Face> face, const fib::NextHop& next)
{
return id == next.getFace()->getId() && next.getCost() == cost && face == next.getFace();
}
bool
addedNextHopWithFace(const Fib& fib, const Name& prefix, size_t oldSize,
uint32_t cost, shared_ptr<Face> face)
{
shared_ptr<fib::Entry> entry = fib.findExactMatch(prefix);
if (static_cast<bool>(entry))
{
const fib::NextHopList& hops = entry->getNextHops();
return hops.size() == oldSize + 1 &&
std::find_if(hops.begin(), hops.end(), bind(&foundNextHop, -1, cost, _1)) != hops.end();
}
return false;
}
BOOST_AUTO_TEST_CASE(TestFireInterestFilter)
{
shared_ptr<InternalFace> face = getInternalFace();
Interest command("/localhost/nfd/fib");
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, this, _1,
command.getName(), 400, "Malformed command");
face->sendInterest(command);
BOOST_REQUIRE(didCallbackFire());
}
BOOST_AUTO_TEST_CASE(MalformedCommmand)
{
shared_ptr<InternalFace> face = getInternalFace();
BOOST_REQUIRE(didCallbackFire() == false);
Interest command("/localhost/nfd/fib");
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, this, _1,
command.getName(), 400, "Malformed command");
getFibManager().onFibRequest(command);
BOOST_REQUIRE(didCallbackFire());
}
BOOST_AUTO_TEST_CASE(UnsupportedVerb)
{
shared_ptr<InternalFace> face = getInternalFace();
FibManagementOptions options;
options.setName("/hello");
options.setFaceId(1);
options.setCost(1);
Block encodedOptions(options.wireEncode());
Name commandName("/localhost/nfd/fib");
commandName.append("unsupported");
commandName.append(encodedOptions);
shared_ptr<Interest> command(make_shared<Interest>(commandName));
generateCommand(*command);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, this, _1,
command->getName(), 501, "Unsupported command");
getFibManager().onFibRequest(*command);
BOOST_REQUIRE(didCallbackFire());
}
BOOST_AUTO_TEST_CASE(UnsignedCommand)
{
addFace(make_shared<DummyFace>());
shared_ptr<InternalFace> face = getInternalFace();
FibManagementOptions options;
options.setName("/hello");
options.setFaceId(1);
options.setCost(101);
Block encodedOptions(options.wireEncode());
Name commandName("/localhost/nfd/fib");
commandName.append("add-nexthop");
commandName.append(encodedOptions);
Interest command(commandName);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse,
this, _1, command.getName(), 401, "Signature required");
getFibManager().onFibRequest(command);
BOOST_REQUIRE(didCallbackFire());
BOOST_REQUIRE(!addedNextHopWithCost(getFib(), "/hello", 0, 101));
}
BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand, UnauthorizedCommandFixture<FibManagerFixture>)
{
addFace(make_shared<DummyFace>());
shared_ptr<InternalFace> face = getInternalFace();
FibManagementOptions options;
options.setName("/hello");
options.setFaceId(1);
options.setCost(101);
Block encodedOptions(options.wireEncode());
Name commandName("/localhost/nfd/fib");
commandName.append("add-nexthop");
commandName.append(encodedOptions);
shared_ptr<Interest> command(make_shared<Interest>(commandName));
generateCommand(*command);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse,
this, _1, command->getName(), 403, "Unauthorized command");
getFibManager().onFibRequest(*command);
BOOST_REQUIRE(didCallbackFire());
BOOST_REQUIRE(!addedNextHopWithCost(getFib(), "/hello", 0, 101));
}
BOOST_AUTO_TEST_CASE(BadOptionParse)
{
addFace(make_shared<DummyFace>());
shared_ptr<InternalFace> face = getInternalFace();
Name commandName("/localhost/nfd/fib");
commandName.append("add-nexthop");
commandName.append("NotReallyOptions");
shared_ptr<Interest> command(make_shared<Interest>(commandName));
generateCommand(*command);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, this, _1,
command->getName(), 400, "Malformed command");
getFibManager().onFibRequest(*command);
BOOST_REQUIRE(didCallbackFire());
}
BOOST_AUTO_TEST_CASE(UnknownFaceId)
{
addFace(make_shared<DummyFace>());
shared_ptr<InternalFace> face = getInternalFace();
FibManagementOptions options;
options.setName("/hello");
options.setFaceId(1000);
options.setCost(101);
Block encodedOptions(options.wireEncode());
Name commandName("/localhost/nfd/fib");
commandName.append("add-nexthop");
commandName.append(encodedOptions);
shared_ptr<Interest> command(make_shared<Interest>(commandName));
generateCommand(*command);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, this, _1,
command->getName(), 404, "Face not found");
getFibManager().onFibRequest(*command);
BOOST_REQUIRE(didCallbackFire());
BOOST_REQUIRE(addedNextHopWithCost(getFib(), "/hello", 0, 101) == false);
}
BOOST_AUTO_TEST_CASE(TestImplicitFaceId)
{
addFace(make_shared<DummyFace>());
shared_ptr<InternalFace> face = getInternalFace();
FibManagementOptions options;
options.setName("/hello");
options.setFaceId(0);
options.setCost(101);
Block encodedOptions(options.wireEncode());
Name commandName("/localhost/nfd/fib");
commandName.append("add-nexthop");
commandName.append(encodedOptions);
FibManagementOptions expectedOptions;
expectedOptions.setName("/hello");
expectedOptions.setFaceId(1);
expectedOptions.setCost(101);
Block encodedExpectedOptions(expectedOptions.wireEncode());
shared_ptr<Interest> command(make_shared<Interest>(commandName));
command->setIncomingFaceId(1);
generateCommand(*command);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, this, _1,
command->getName(), 200, "Success", encodedExpectedOptions);
getFib().insert("/hello");
getFibManager().onFibRequest(*command);
BOOST_REQUIRE(didCallbackFire());
BOOST_REQUIRE(addedNextHopWithFace(getFib(), "/hello", 0, 101, getFace(1)));
}
BOOST_AUTO_TEST_CASE(AddNextHopVerbInitialAdd)
{
addFace(make_shared<DummyFace>());
shared_ptr<InternalFace> face = getInternalFace();
FibManagementOptions options;
options.setName("/hello");
options.setFaceId(1);
options.setCost(101);
Block encodedOptions(options.wireEncode());
Name commandName("/localhost/nfd/fib");
commandName.append("add-nexthop");
commandName.append(encodedOptions);
shared_ptr<Interest> command(make_shared<Interest>(commandName));
generateCommand(*command);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, this, _1,
command->getName(), 200, "Success", encodedOptions);
getFib().insert("/hello");
getFibManager().onFibRequest(*command);
BOOST_REQUIRE(didCallbackFire());
BOOST_REQUIRE(addedNextHopWithCost(getFib(), "/hello", 0, 101));
}
BOOST_AUTO_TEST_CASE(AddNextHopVerbAddToExisting)
{
addFace(make_shared<DummyFace>());
shared_ptr<InternalFace> face = getInternalFace();
getFib().insert("/hello");
for (int i = 1; i <= 2; i++)
{
FibManagementOptions options;
options.setName("/hello");
options.setFaceId(1);
options.setCost(100 + i);
Block encodedOptions(options.wireEncode());
Name commandName("/localhost/nfd/fib");
commandName.append("add-nexthop");
commandName.append(encodedOptions);
shared_ptr<Interest> command(make_shared<Interest>(commandName));
generateCommand(*command);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, this, _1,
command->getName(), 200, "Success", encodedOptions);
getFibManager().onFibRequest(*command);
BOOST_REQUIRE(didCallbackFire());
resetCallbackFired();
shared_ptr<fib::Entry> entry = getFib().findExactMatch("/hello");
if (static_cast<bool>(entry))
{
const fib::NextHopList& hops = entry->getNextHops();
BOOST_REQUIRE(hops.size() == 1);
BOOST_REQUIRE(std::find_if(hops.begin(), hops.end(),
bind(&foundNextHop, -1, 100 + i, _1)) != hops.end());
}
else
{
BOOST_FAIL("Failed to find expected fib entry");
}
face->onReceiveData.clear();
}
}
BOOST_AUTO_TEST_CASE(AddNextHopVerbUpdateFaceCost)
{
addFace(make_shared<DummyFace>());
shared_ptr<InternalFace> face = getInternalFace();
getFib().insert("/hello");
FibManagementOptions options;
options.setName("/hello");
options.setFaceId(1);
{
options.setCost(1);
Block encodedOptions(options.wireEncode());
Name commandName("/localhost/nfd/fib");
commandName.append("add-nexthop");
commandName.append(encodedOptions);
shared_ptr<Interest> command(make_shared<Interest>(commandName));
generateCommand(*command);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, this, _1,
command->getName(), 200, "Success", encodedOptions);
getFibManager().onFibRequest(*command);
BOOST_REQUIRE(didCallbackFire());
}
resetCallbackFired();
face->onReceiveData.clear();
{
options.setCost(102);
Block encodedOptions(options.wireEncode());
Name commandName("/localhost/nfd/fib");
commandName.append("add-nexthop");
commandName.append(encodedOptions);
shared_ptr<Interest> command(make_shared<Interest>(commandName));
generateCommand(*command);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, this, _1,
command->getName(), 200, "Success", encodedOptions);
getFibManager().onFibRequest(*command);
BOOST_REQUIRE(didCallbackFire());
}
shared_ptr<fib::Entry> entry = getFib().findExactMatch("/hello");
// Add faces with cost == FaceID for the name /hello
// This test assumes:
// FaceIDs are -1 because we don't add them to a forwarder
if (static_cast<bool>(entry))
{
const fib::NextHopList& hops = entry->getNextHops();
BOOST_REQUIRE(hops.size() == 1);
BOOST_REQUIRE(std::find_if(hops.begin(),
hops.end(),
bind(&foundNextHop, -1, 102, _1)) != hops.end());
}
else
{
BOOST_FAIL("Failed to find expected fib entry");
}
}
BOOST_AUTO_TEST_CASE(Insert)
{
shared_ptr<InternalFace> face = getInternalFace();
{
FibManagementOptions options;
options.setName("/hello");
Block encodedOptions(options.wireEncode());
Name commandName("/localhost/nfd/fib");
commandName.append("insert");
commandName.append(encodedOptions);
shared_ptr<Interest> command(make_shared<Interest>(commandName));
generateCommand(*command);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, this, _1,
command->getName(), 200, "Success", encodedOptions);
getFibManager().onFibRequest(*command);
}
BOOST_REQUIRE(didCallbackFire());
shared_ptr<fib::Entry> entry = getFib().findExactMatch("/hello");
if (static_cast<bool>(entry))
{
const fib::NextHopList& hops = entry->getNextHops();
BOOST_CHECK_EQUAL(hops.size(), 0);
}
resetCallbackFired();
face->onReceiveData.clear();
{
FibManagementOptions options;
options.setName("/hello");
Block encodedOptions(options.wireEncode());
Name commandName("/localhost/nfd/fib");
commandName.append("insert");
commandName.append(encodedOptions);
shared_ptr<Interest> command(make_shared<Interest>(commandName));
generateCommand(*command);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, this, _1,
command->getName(), 200, "Success", encodedOptions);
getFibManager().onFibRequest(*command);
}
BOOST_REQUIRE(didCallbackFire());
entry = getFib().findExactMatch("/hello");
if (static_cast<bool>(entry))
{
const fib::NextHopList& hops = entry->getNextHops();
BOOST_CHECK_EQUAL(hops.size(), 0);
}
}
void
testRemove(CommandFixture<FibManagerFixture>* fixture,
FibManager& manager,
Fib& fib,
shared_ptr<Face> face,
const Name& target)
{
FibManagementOptions options;
options.setName(target);
Block encodedOptions(options.wireEncode());
Name commandName("/localhost/nfd/fib");
commandName.append("delete");
commandName.append(encodedOptions);
shared_ptr<Interest> command(make_shared<Interest>(commandName));
fixture->generateCommand(*command);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, fixture, _1,
command->getName(), 200, "Success", encodedOptions);
manager.onFibRequest(*command);
BOOST_REQUIRE(fixture->didCallbackFire());
if (static_cast<bool>(fib.findExactMatch(target)))
{
BOOST_FAIL("Found \"removed\" prefix");
}
face->onReceiveData.clear();
}
BOOST_AUTO_TEST_CASE(Delete)
{
shared_ptr<InternalFace> face = getInternalFace();
FibManager& manager = getFibManager();
Fib& fib = getFib();
fib.insert("/a");
fib.insert("/a/b");
fib.insert("/a/b/c");
testRemove(this, manager, fib, face, "/");
if (!static_cast<bool>(fib.findExactMatch("/a")) ||
!static_cast<bool>(fib.findExactMatch("/a/b")) ||
!static_cast<bool>(fib.findExactMatch("/a/b/c")))
{
BOOST_FAIL("Removed incorrect entry");
}
testRemove(this, manager, fib, face, "/a/b");
if (!static_cast<bool>(fib.findExactMatch("/a")) ||
!static_cast<bool>(fib.findExactMatch("/a/b/c")))
{
BOOST_FAIL("Removed incorrect entry");
}
testRemove(this, manager, fib, face, "/a/b/c");
if (!static_cast<bool>(fib.findExactMatch("/a")))
{
BOOST_FAIL("Removed incorrect entry");
}
testRemove(this, manager, fib, face, "/a");
testRemove(this, manager, fib, face, "/does/not/exist");
}
bool
removedNextHopWithCost(const Fib& fib, const Name& prefix, size_t oldSize, uint32_t cost)
{
shared_ptr<fib::Entry> entry = fib.findExactMatch(prefix);
if (static_cast<bool>(entry))
{
const fib::NextHopList& hops = entry->getNextHops();
return hops.size() == oldSize - 1 &&
std::find_if(hops.begin(), hops.end(), bind(&foundNextHop, -1, cost, _1)) == hops.end();
}
return false;
}
void
testRemoveNextHop(CommandFixture<FibManagerFixture>* fixture,
FibManager& manager,
Fib& fib,
shared_ptr<Face> face,
const Name& targetName,
FaceId targetFace)
{
FibManagementOptions options;
options.setName(targetName);
options.setFaceId(targetFace);
Block encodedOptions(options.wireEncode());
Name commandName("/localhost/nfd/fib");
commandName.append("remove-nexthop");
commandName.append(encodedOptions);
shared_ptr<Interest> command(make_shared<Interest>(commandName));
fixture->generateCommand(*command);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, fixture, _1,
command->getName(), 200, "Success", encodedOptions);
manager.onFibRequest(*command);
BOOST_REQUIRE(fixture->didCallbackFire());
fixture->resetCallbackFired();
face->onReceiveData.clear();
}
BOOST_AUTO_TEST_CASE(RemoveNextHop)
{
shared_ptr<Face> face1 = make_shared<DummyFace>();
shared_ptr<Face> face2 = make_shared<DummyFace>();
shared_ptr<Face> face3 = make_shared<DummyFace>();
addFace(face1);
addFace(face2);
addFace(face3);
shared_ptr<InternalFace> face = getInternalFace();
FibManager& manager = getFibManager();
Fib& fib = getFib();
shared_ptr<fib::Entry> entry = fib.insert("/hello").first;
entry->addNextHop(face1, 101);
entry->addNextHop(face2, 202);
entry->addNextHop(face3, 303);
testRemoveNextHop(this, manager, fib, face, "/hello", 2);
BOOST_REQUIRE(removedNextHopWithCost(fib, "/hello", 3, 202));
testRemoveNextHop(this, manager, fib, face, "/hello", 3);
BOOST_REQUIRE(removedNextHopWithCost(fib, "/hello", 2, 303));
testRemoveNextHop(this, manager, fib, face, "/hello", 1);
BOOST_REQUIRE(removedNextHopWithCost(fib, "/hello", 1, 101));
if (!static_cast<bool>(getFib().findExactMatch("/hello")))
{
BOOST_FAIL("removed entry after removing all next hops");
}
}
BOOST_AUTO_TEST_CASE(RemoveNoFace)
{
shared_ptr<InternalFace> face = getInternalFace();
FibManagementOptions options;
options.setName("/hello");
options.setFaceId(1);
Block encodedOptions(options.wireEncode());
Name commandName("/localhost/nfd/fib");
commandName.append("remove-nexthop");
commandName.append(encodedOptions);
shared_ptr<Interest> command(make_shared<Interest>(commandName));
generateCommand(*command);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, this, _1,
command->getName(), 404, "Face not found");
getFibManager().onFibRequest(*command);
BOOST_REQUIRE(didCallbackFire());
}
BOOST_AUTO_TEST_CASE(RemoveNoPrefix)
{
addFace(make_shared<DummyFace>());
shared_ptr<InternalFace> face = getInternalFace();
FibManagementOptions options;
options.setName("/hello");
options.setFaceId(1);
Block encodedOptions(options.wireEncode());
Name commandName("/localhost/nfd/fib");
commandName.append("remove-nexthop");
commandName.append(encodedOptions);
shared_ptr<Interest> command(make_shared<Interest>(commandName));
generateCommand(*command);
face->onReceiveData +=
bind(&FibManagerFixture::validateControlResponse, this, _1,
command->getName(), 404, "Prefix not found");
getFibManager().onFibRequest(*command);
BOOST_REQUIRE(didCallbackFire());
}
BOOST_AUTO_TEST_SUITE_END()
} // namespace tests
} // namespace nfd