/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * Copyright (c) 2014-2023, The University of Memphis, * Regents of the University of California, * Arizona Board of Regents. * * This file is part of NLSR (Named-data Link State Routing). * See AUTHORS.md for complete list of NLSR authors and contributors. * * NLSR 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. * * NLSR 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 * NLSR, e.g., in COPYING.md file. If not, see . */ #include "lsdb.hpp" #include "lsa/lsa.hpp" #include "name-prefix-list.hpp" #include "tests/io-key-chain-fixture.hpp" #include "tests/test-common.hpp" #include #include #include #include namespace nlsr { namespace test { class LsdbFixture : public IoKeyChainFixture { public: LsdbFixture() : face(m_io, m_keyChain, {true, true}) , conf(face, m_keyChain) , confProcessor(conf) , lsdb(face, m_keyChain, conf) { m_keyChain.createIdentity("/ndn/site/%C1.Router/this-router"); advanceClocks(10_ms); face.sentInterests.clear(); } void isFirstNameLsaEqual(const Lsdb& otherLsdb) { auto selfLsaRange = lsdb.getLsdbIterator(); auto otherLsaRange = otherLsdb.getLsdbIterator(); if (selfLsaRange.first != selfLsaRange.second && otherLsaRange.first != otherLsaRange.second) { auto ownLsa = std::static_pointer_cast(*selfLsaRange.first); auto otherLsa = std::static_pointer_cast(*otherLsaRange.first); BOOST_CHECK_EQUAL(ownLsa->getNpl(), otherLsa->getNpl()); return; } BOOST_CHECK(false); } void checkSignalResult(LsdbUpdate updateType, const std::shared_ptr& lsaPtr, const std::list& namesToAdd, const std::list& namesToRemove) { BOOST_CHECK(updateHappened); BOOST_CHECK_EQUAL(lsaPtrCheck->getOriginRouter(), lsaPtr->getOriginRouter()); BOOST_CHECK_EQUAL(lsaPtrCheck->getType(), lsaPtr->getType()); BOOST_CHECK(updateType == updateTypeCheck); BOOST_CHECK(namesToAdd == namesToAddCheck); BOOST_CHECK(namesToRemove == namesToRemoveCheck); updateHappened = false; } void connectSignal() { lsdb.onLsdbModified.connect( [&] (std::shared_ptr lsa, LsdbUpdate updateType, const auto& namesToAdd, const auto& namesToRemove) { lsaPtrCheck = lsa; updateTypeCheck = updateType; namesToAddCheck = namesToAdd; namesToRemoveCheck = namesToRemove; updateHappened = true; } ); } public: ndn::DummyClientFace face; ConfParameter conf; DummyConfFileProcessor confProcessor; Lsdb lsdb; LsdbUpdate updateTypeCheck = LsdbUpdate::INSTALLED; std::list namesToAddCheck; std::list namesToRemoveCheck; std::shared_ptr lsaPtrCheck = nullptr; bool updateHappened = false; }; BOOST_FIXTURE_TEST_SUITE(TestLsdb, LsdbFixture) BOOST_AUTO_TEST_CASE(LsdbSync) { ndn::Name interestName("/ndn/NLSR/LSA/cs/%C1.Router/router2/name"); uint64_t oldSeqNo = 82; ndn::Name oldInterestName = interestName; oldInterestName.appendNumber(oldSeqNo); lsdb.expressInterest(oldInterestName, 0, 0); advanceClocks(10_ms); std::vector& interests = face.sentInterests; BOOST_REQUIRE(interests.size() > 0); bool didFindInterest = false; for (const auto& interest : interests) { didFindInterest = didFindInterest || interest.getName() == oldInterestName; } BOOST_CHECK(didFindInterest); interests.clear(); auto deadline = ndn::time::steady_clock::now() + ndn::time::seconds(LSA_REFRESH_TIME_MAX); // Simulate an LSA interest timeout lsdb.onFetchLsaError(ndn::SegmentFetcher::ErrorCode::INTEREST_TIMEOUT, "Timeout", oldInterestName, 0, deadline, interestName, oldSeqNo); advanceClocks(10_ms); BOOST_REQUIRE(interests.size() > 0); didFindInterest = false; for (const auto& interest : interests) { didFindInterest = didFindInterest || interest.getName() == oldInterestName; } BOOST_CHECK(didFindInterest); interests.clear(); uint64_t newSeqNo = 83; ndn::Name newInterestName = interestName; newInterestName.appendNumber(newSeqNo); lsdb.expressInterest(newInterestName, 0, 0); advanceClocks(10_ms); BOOST_REQUIRE(interests.size() > 0); didFindInterest = false; for (const auto& interest : interests) { didFindInterest = didFindInterest || interest.getName() == newInterestName; } BOOST_CHECK(didFindInterest); interests.clear(); // Simulate an LSA interest timeout where the sequence number is outdated lsdb.onFetchLsaError(ndn::SegmentFetcher::ErrorCode::INTEREST_TIMEOUT, "Timeout", oldInterestName, 0, deadline, interestName, oldSeqNo); advanceClocks(10_ms); // Interest should not be expressed for outdated sequence number BOOST_CHECK_EQUAL(interests.size(), 0); } BOOST_AUTO_TEST_CASE(LsdbSegmentedData) { // Add a lot of NameLSAs to exceed max packet size ndn::Name originRouter("/ndn/site/%C1.Router/this-router"); auto nameLsa = lsdb.findLsa(originRouter); BOOST_REQUIRE(nameLsa != nullptr); uint64_t seqNo = nameLsa->getSeqNo(); ndn::Name prefix("/ndn/edu/memphis/netlab/research/nlsr/test/prefix/"); int nPrefixes = 0; while (nameLsa->wireEncode().size() < ndn::MAX_NDN_PACKET_SIZE) { nameLsa->addName(ndn::Name(prefix).appendNumber(++nPrefixes)); break; } lsdb.installLsa(nameLsa); // Create another Lsdb and expressInterest ndn::DummyClientFace face2(m_io, m_keyChain, {true, true}); face.linkTo(face2); ConfParameter conf2(face2, m_keyChain); std::string config = R"CONF( trust-anchor { type any } )CONF"; conf2.getValidator().load(config, "config-file-from-string"); Lsdb lsdb2(face2, m_keyChain, conf2); advanceClocks(ndn::time::milliseconds(10), 10); ndn::Name interestName("/localhop/ndn/nlsr/LSA/site/%C1.Router/this-router/NAME"); interestName.appendNumber(seqNo); lsdb2.expressInterest(interestName, 0/*= timeout count*/, 0); advanceClocks(ndn::time::milliseconds(200), 20); isFirstNameLsaEqual(lsdb2); } BOOST_AUTO_TEST_CASE(SegmentLsaData) { ndn::Name originRouter("/ndn/site/%C1.Router/this-router"); auto lsa = lsdb.findLsa(originRouter); uint64_t seqNo = lsa->getSeqNo(); ndn::Name prefix("/ndn/edu/memphis/netlab/research/nlsr/test/prefix/"); int nPrefixes = 0; while (lsa->wireEncode().size() < ndn::MAX_NDN_PACKET_SIZE) { lsa->addName(ndn::Name(prefix).appendNumber(++nPrefixes)); } lsdb.installLsa(lsa); ndn::Block expectedDataContent = lsa->wireEncode(); ndn::Name interestName("/localhop/ndn/nlsr/LSA/site/%C1.Router/this-router/NAME/"); interestName.appendNumber(seqNo); ndn::DummyClientFace face2(m_io, m_keyChain, {true, true}); face.linkTo(face2); auto fetcher = ndn::SegmentFetcher::start(face2, ndn::Interest(interestName), ndn::security::getAcceptAllValidator()); fetcher->onComplete.connect([&expectedDataContent] (ndn::ConstBufferPtr bufferPtr) { ndn::Block block(bufferPtr); BOOST_CHECK_EQUAL(expectedDataContent, block); }); advanceClocks(ndn::time::milliseconds(1), 100); fetcher->stop(); } BOOST_AUTO_TEST_CASE(ReceiveSegmentedLsaData) { ndn::Name router("/ndn/cs/%C1.Router/router1"); uint64_t seqNo = 12; NamePrefixList prefixList; NameLsa lsa(router, seqNo, ndn::time::system_clock::now(), prefixList); ndn::Name prefix("/prefix/"); for (int nPrefixes = 0; nPrefixes < 3; ++nPrefixes) { lsa.addName(ndn::Name(prefix).appendNumber(nPrefixes)); } ndn::Name interestName("/localhop/ndn/nlsr/LSA/cs/%C1.Router/router1/NAME/"); interestName.appendNumber(seqNo); ndn::Block block = lsa.wireEncode(); lsdb.afterFetchLsa(block.getBuffer(), interestName); auto foundLsa = std::static_pointer_cast(lsdb.findLsa(lsa.getOriginRouter(), lsa.getType())); BOOST_REQUIRE(foundLsa != nullptr); BOOST_CHECK_EQUAL(foundLsa->wireEncode(), lsa.wireEncode()); } BOOST_AUTO_TEST_CASE(LsdbRemoveAndExists) { auto testTimePoint = ndn::time::system_clock::now(); NamePrefixList npl1; std::string s1 = "name1"; std::string s2 = "name2"; std::string router1 = "/router1/1"; npl1.insert(s1); npl1.insert(s2); // For NameLsa lsType is name. // 12 is seqNo, randomly generated. // 1800 seconds is the default life time. NameLsa nlsa1(router1, 12, testTimePoint, npl1); Lsdb& lsdb1(lsdb); lsdb1.installLsa(std::make_shared(nlsa1)); BOOST_CHECK(lsdb1.doesLsaExist(router1, Lsa::Type::NAME)); lsdb1.removeLsa(router1, Lsa::Type::NAME); BOOST_CHECK_EQUAL(lsdb1.doesLsaExist(router1, Lsa::Type::NAME), false); } BOOST_AUTO_TEST_CASE(InstallNameLsa) { // Install lsa with name1 and name2 ndn::Name name1("/ndn/name1"); ndn::Name name2("/ndn/name2"); NamePrefixList prefixes; prefixes.insert(name1); prefixes.insert(name2); std::string otherRouter("/ndn/site/%C1.router/other-router"); const auto MAX_TIME = ndn::time::system_clock::time_point::max(); NameLsa lsa(otherRouter, 1, MAX_TIME, prefixes); lsdb.installLsa(std::make_shared(lsa)); BOOST_REQUIRE_EQUAL(lsdb.doesLsaExist(otherRouter, Lsa::Type::NAME), true); NamePrefixList& nameList = std::static_pointer_cast(lsdb.findLsa(otherRouter, Lsa::Type::NAME))->getNpl(); BOOST_CHECK_EQUAL(nameList, prefixes); // Add a prefix: name3 ndn::Name name3("/ndn/name3"); prefixes.insert(name3); NameLsa addLsa(otherRouter, 2, MAX_TIME, prefixes); lsdb.installLsa(std::make_shared(addLsa)); // Lsa should include name1, name2, and name3 BOOST_CHECK_EQUAL(nameList, prefixes); // Remove a prefix: name2 prefixes.erase(name2); NameLsa removeLsa(otherRouter, 3, MAX_TIME, prefixes); lsdb.installLsa(std::make_shared(removeLsa)); // Lsa should include name1 and name3 BOOST_CHECK_EQUAL(nameList, prefixes); // Add and remove a prefix: add name2, remove name3 prefixes.insert(name2); prefixes.erase(name3); NameLsa addAndRemoveLsa(otherRouter, 4, MAX_TIME, prefixes); lsdb.installLsa(std::make_shared(addAndRemoveLsa)); // Lsa should include name1 and name2 BOOST_CHECK_EQUAL(nameList, prefixes); // Install a completely new list of prefixes ndn::Name name4("/ndn/name4"); ndn::Name name5("/ndn/name5"); NamePrefixList newPrefixes; newPrefixes.insert(name4); newPrefixes.insert(name5); NameLsa newLsa(otherRouter, 5, MAX_TIME, newPrefixes); lsdb.installLsa(std::make_shared(newLsa)); // Lsa should include name4 and name5 BOOST_CHECK_EQUAL(nameList, newPrefixes); } BOOST_AUTO_TEST_CASE(TestIsLsaNew) { ndn::Name originRouter("/ndn/memphis/%C1.Router/other-router"); // Install Name LSA NamePrefixList nameList; NameLsa lsa(originRouter, 999, ndn::time::system_clock::time_point::max(), nameList); lsdb.installLsa(std::make_shared(lsa)); // Lower NameLSA sequence number uint64_t lowerSeqNo = 998; BOOST_CHECK(!lsdb.isLsaNew(originRouter, Lsa::Type::NAME, lowerSeqNo)); // Same NameLSA sequence number uint64_t sameSeqNo = 999; BOOST_CHECK(!lsdb.isLsaNew(originRouter, Lsa::Type::NAME, sameSeqNo)); // Higher NameLSA sequence number uint64_t higherSeqNo = 1000; BOOST_CHECK(lsdb.isLsaNew(originRouter, Lsa::Type::NAME, higherSeqNo)); } BOOST_AUTO_TEST_CASE(LsdbSignals) { connectSignal(); auto testTimePoint = ndn::time::system_clock::now() + 3600_s; ndn::Name router2("/router2"); AdjLsa adjLsa(router2, 12, testTimePoint, 2, conf.getAdjacencyList()); std::shared_ptr lsaPtr = std::make_shared(adjLsa); lsdb.installLsa(lsaPtr); checkSignalResult(LsdbUpdate::INSTALLED, lsaPtr, {}, {}); adjLsa.setSeqNo(13); updateHappened = false; lsaPtr = std::make_shared(adjLsa); lsdb.installLsa(lsaPtr); BOOST_CHECK(!updateHappened); Adjacent adj("Neighbor1"); adjLsa.setSeqNo(14); adjLsa.addAdjacent(adj); lsaPtr = std::make_shared(adjLsa); lsdb.installLsa(lsaPtr); checkSignalResult(LsdbUpdate::UPDATED, lsaPtr, {}, {}); lsdb.removeLsa(lsaPtrCheck->getOriginRouter(), Lsa::Type::ADJACENCY); checkSignalResult(LsdbUpdate::REMOVED, lsaPtr, {}, {}); // Name LSA NamePrefixList npl1{"name1", "name2"}; NameLsa nameLsa(router2, 12, testTimePoint, npl1); lsaPtr = std::make_shared(nameLsa); lsdb.installLsa(lsaPtr); checkSignalResult(LsdbUpdate::INSTALLED, lsaPtr, {}, {}); nameLsa.setSeqNo(13); lsaPtr = std::make_shared(nameLsa); lsdb.installLsa(lsaPtr); BOOST_CHECK(!updateHappened); NamePrefixList npl2{"name2", "name3"}; NameLsa nameLsa2(router2, 14, testTimePoint, npl2); lsaPtr = std::make_shared(nameLsa2); lsdb.installLsa(lsaPtr); checkSignalResult(LsdbUpdate::UPDATED, lsaPtr, {"name3"}, {"name1"}); lsdb.removeLsa(lsaPtrCheck->getOriginRouter(), Lsa::Type::NAME); checkSignalResult(LsdbUpdate::REMOVED, lsaPtr, {}, {}); // Coordinate LSA lsaPtr = std::make_shared(CoordinateLsa("router1", 12, testTimePoint, 2.5, {30})); lsdb.installLsa(lsaPtr); checkSignalResult(LsdbUpdate::INSTALLED, lsaPtr, {}, {}); lsaPtr = std::make_shared(CoordinateLsa("router1", 13, testTimePoint, 2.5, {30})); lsdb.installLsa(lsaPtr); BOOST_CHECK(!updateHappened); lsaPtr = std::make_shared(CoordinateLsa("router1", 14, testTimePoint, 2.5, {40})); lsdb.installLsa(lsaPtr); checkSignalResult(LsdbUpdate::UPDATED, lsaPtr, {}, {}); lsdb.removeLsa(lsaPtrCheck->getOriginRouter(), Lsa::Type::COORDINATE); checkSignalResult(LsdbUpdate::REMOVED, lsaPtr, {}, {}); } BOOST_AUTO_TEST_SUITE_END() // TestLsdb } // namespace test } // namespace nlsr