blob: 513536ce0df603b6738d3ea634981a3d55d8389c [file] [log] [blame]
Yanbiao Li711c7932015-08-19 16:30:16 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2015, Regents of the University of California,
4 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis.
10 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24 */
25#include "mgmt/fib-manager.hpp"
26
27#include "manager-common-fixture.hpp"
28#include "table/fib-nexthop.hpp"
29#include "../face/dummy-face.hpp"
30#include <ndn-cxx/management/nfd-fib-entry.hpp>
31
32namespace nfd {
33namespace tests {
34
35class FibManagerFixture : public ManagerCommonFixture
36{
37public:
38 FibManagerFixture()
39 : m_fib(m_forwarder.getFib())
40 , m_faceTable(m_forwarder.getFaceTable())
41 , m_manager(m_fib, bind(&Forwarder::getFace, &m_forwarder, _1), m_dispatcher, m_validator)
42 {
43 setTopPrefixAndPrivilege("/localhost/nfd", "fib");
44 }
45
46public: // for test
47 ControlParameters
48 makeParameters(const Name& name, const FaceId& id)
49 {
50 return ControlParameters().setName(name).setFaceId(id);
51 }
52
53 ControlParameters
54 makeParameters(const Name& name, const FaceId& id, const uint32_t& cost)
55 {
56 return ControlParameters().setName(name).setFaceId(id).setCost(cost);
57 }
58
59 FaceId
60 addFace()
61 {
62 auto face = make_shared<DummyFace>();
63 m_faceTable.add(face);
64 advanceClocks(time::milliseconds(1), 10);
65 m_responses.clear(); // clear all event notifications, if any
66 return face->getId();
67 }
68
69public: // for check
70 enum class CheckNextHopResult
71 {
72 OK,
73 NO_FIB_ENTRY,
74 WRONG_N_NEXTHOPS,
75 NO_NEXTHOP,
76 WRONG_COST
77 };
78
79 /**
80 * @brief check whether the nexthop record is added / removed properly
81 *
82 * @param expectedNNextHops use -1 to skip this check
83 * @param faceId use FACEID_NULL to skip NextHopRecord checks
84 * @param expectedCost use -1 to skip this check
85 *
86 * @retval OK FIB entry is found by exact match and has the expected number of nexthops;
87 * NextHopRe record for faceId is found and has the expected cost
88 * @retval NO_FIB_ENTRY FIB entry is not found
89 * @retval WRONG_N_NEXTHOPS FIB entry is found but has wrong number of nexthops
90 * @retval NO_NEXTHOP NextHopRecord for faceId is not found
91 * @retval WRONG_COST NextHopRecord for faceId has wrong cost
92 */
93 CheckNextHopResult
94 checkNextHop(const Name& prefix, ssize_t expectedNNextHops = -1,
95 FaceId faceId = FACEID_NULL, int32_t expectedCost = -1)
96 {
97 auto entry = m_fib.findExactMatch(prefix);
98 if (!static_cast<bool>(entry)) {
99 return CheckNextHopResult::NO_FIB_ENTRY;
100 }
101
102 auto nextHops = entry->getNextHops();
103 if (expectedNNextHops != -1 && nextHops.size() != static_cast<size_t>(expectedNNextHops)) {
104 return CheckNextHopResult::WRONG_N_NEXTHOPS;
105 }
106
107 if (faceId != FACEID_NULL) {
108 for (auto&& record : nextHops) {
109 if (record.getFace()->getId() == faceId) {
110 return expectedCost != -1 && record.getCost() != static_cast<uint32_t>(expectedCost) ?
111 CheckNextHopResult::WRONG_COST : CheckNextHopResult::OK;
112 }
113 }
114
115 return CheckNextHopResult::NO_NEXTHOP;
116 }
117
118 return CheckNextHopResult::OK;
119 }
120
121protected:
122 Fib& m_fib;
123 FaceTable& m_faceTable;
124 FibManager m_manager;
125};
126
127std::ostream&
128operator<<(std::ostream &os, const FibManagerFixture::CheckNextHopResult& result)
129{
130 switch (result) {
131 case FibManagerFixture::CheckNextHopResult::OK:
132 os << "OK";
133 break;
134 case FibManagerFixture::CheckNextHopResult::NO_FIB_ENTRY:
135 os << "NO_FIB_ENTRY";
136 break;
137 case FibManagerFixture::CheckNextHopResult::WRONG_N_NEXTHOPS:
138 os << "WRONG_N_NEXTHOPS";
139 break;
140 case FibManagerFixture::CheckNextHopResult::NO_NEXTHOP:
141 os << "NO_NEXTHOP";
142 break;
143 case FibManagerFixture::CheckNextHopResult::WRONG_COST:
144 os << "WRONG_COST";
145 break;
146 default:
147 break;
148 };
149
150 return os;
151}
152
153BOOST_FIXTURE_TEST_SUITE(Mgmt, FibManagerFixture)
154BOOST_AUTO_TEST_SUITE(TestFibManager)
155
156BOOST_AUTO_TEST_SUITE(AddNextHop)
157
158BOOST_AUTO_TEST_CASE(UnknownFaceId)
159{
160 auto command = makeControlCommandRequest("/localhost/nfd/fib/add-nexthop",
161 makeParameters("hello", FACEID_NULL, 101));
162 receiveInterest(command);
163 BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
164
165 // check response
166 BOOST_CHECK_EQUAL(checkResponse(0, command->getName(), ControlResponse(410, "Face not found")),
167 CheckResponseResult::OK);
168
169 // double check that the next hop was not added
170 BOOST_CHECK_EQUAL(checkNextHop("/hello", -1, FACEID_NULL, 101), CheckNextHopResult::NO_FIB_ENTRY);
171}
172
173BOOST_AUTO_TEST_CASE(ImplicitFaceId)
174{
175 auto face1 = addFace();
176 auto face2 = addFace();
177 BOOST_REQUIRE(face1 != INVALID_FACEID && face2 != INVALID_FACEID);
178
179 Name expectedName;
180 ControlResponse expectedResponse;
181 auto testAddNextHop = [&] (ControlParameters parameters, const FaceId& faceId) {
182 auto command = makeControlCommandRequest("/localhost/nfd/fib/add-nexthop", parameters,
183 [&faceId] (shared_ptr<Interest> interest) {
184 interest->setIncomingFaceId(faceId);
185 });
186 m_responses.clear();
187 expectedName = command->getName();
188 expectedResponse = makeResponse(200, "Success", parameters.setFaceId(faceId));
189 receiveInterest(command);
190 };
191
192 testAddNextHop(ControlParameters().setName("/hello").setCost(100).setFaceId(0), face1);
193 BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
194 BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
195 BOOST_CHECK_EQUAL(checkNextHop("/hello", 1, face1, 100), CheckNextHopResult::OK);
196
197 testAddNextHop(ControlParameters().setName("/hello").setCost(100), face2);
198 BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
199 BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
200 BOOST_CHECK_EQUAL(checkNextHop("/hello", 2, face2, 100), CheckNextHopResult::OK);
201}
202
203BOOST_AUTO_TEST_CASE(InitialAdd)
204{
205 FaceId addedFaceId = addFace();
206 BOOST_REQUIRE(addedFaceId != INVALID_FACEID);
207
208 auto parameters = makeParameters("hello", addedFaceId, 101);
209 auto command = makeControlCommandRequest("/localhost/nfd/fib/add-nexthop", parameters);
210
211 receiveInterest(command);
212 BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
213 BOOST_CHECK_EQUAL(checkResponse(0, command->getName(), makeResponse(200, "Success", parameters)),
214 CheckResponseResult::OK);
215 BOOST_CHECK_EQUAL(checkNextHop("/hello", 1, addedFaceId, 101), CheckNextHopResult::OK);
216}
217
218BOOST_AUTO_TEST_CASE(ImplicitCost)
219{
220 FaceId addedFaceId = addFace();
221 BOOST_REQUIRE(addedFaceId != INVALID_FACEID);
222
223 auto originalParameters = ControlParameters().setName("/hello").setFaceId(addedFaceId);
224 auto parameters = makeParameters("/hello", addedFaceId, 0);
225 auto command = makeControlCommandRequest("/localhost/nfd/fib/add-nexthop", originalParameters);
226
227 receiveInterest(command);
228 BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
229 BOOST_CHECK_EQUAL(checkResponse(0, command->getName(), makeResponse(200, "Success", parameters)),
230 CheckResponseResult::OK);
231 BOOST_CHECK_EQUAL(checkNextHop("/hello", 1, addedFaceId, 0), CheckNextHopResult::OK);
232}
233
234BOOST_AUTO_TEST_CASE(AddToExisting)
235{
236 FaceId face = addFace();
237 BOOST_CHECK(face != INVALID_FACEID);
238
239 Name expectedName;
240 ControlResponse expectedResponse;
241 auto testAddNextHop = [&] (const ControlParameters& parameters) {
242 m_responses.clear();
243 auto command = makeControlCommandRequest("/localhost/nfd/fib/add-nexthop", parameters);
244 expectedName = command->getName();
245 expectedResponse = makeResponse(200, "Success", parameters);
246 receiveInterest(command);
247 };
248
249 // add initial, succeeds
250 testAddNextHop(makeParameters("/hello", face, 101));
251 BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
252 BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
253
254 // add to existing --> update cost, succeeds
255 testAddNextHop(makeParameters("/hello", face, 102));
256 BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
257 BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
258
259 BOOST_CHECK_EQUAL(checkNextHop("/hello", 2, face, 102), CheckNextHopResult::WRONG_N_NEXTHOPS);
260 BOOST_CHECK_EQUAL(checkNextHop("/hello", 1, face, 101), CheckNextHopResult::WRONG_COST);
261 BOOST_CHECK_EQUAL(checkNextHop("/hello", 1, face, 102), CheckNextHopResult::OK);
262}
263
264BOOST_AUTO_TEST_SUITE_END() // AddNextHop
265
266BOOST_AUTO_TEST_SUITE(RemoveNextHop)
267
268BOOST_AUTO_TEST_CASE(Basic)
269{
270 Name expectedName;
271 ControlResponse expectedResponse;
272 auto testRemoveNextHop = [&] (const ControlParameters& parameters) {
273 m_responses.clear();
274 auto command = makeControlCommandRequest("/localhost/nfd/fib/remove-nexthop", parameters);
275 expectedName = command->getName();
276 expectedResponse = makeResponse(200, "Success", parameters);
277 receiveInterest(command);
278 };
279
280 FaceId face1 = addFace();
281 FaceId face2 = addFace();
282 FaceId face3 = addFace();
283 BOOST_REQUIRE(face1 != INVALID_FACEID && face2 != INVALID_FACEID && face3 != INVALID_FACEID);
284
285 shared_ptr<fib::Entry> entry = m_fib.insert("/hello").first;
286 entry->addNextHop(m_faceTable.get(face1), 101);
287 entry->addNextHop(m_faceTable.get(face2), 202);
288 entry->addNextHop(m_faceTable.get(face3), 303);
289
290 testRemoveNextHop(makeParameters("/hello", face1));
291 BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
292 BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
293 BOOST_CHECK_EQUAL(checkNextHop("/hello", 2, face1, 101), CheckNextHopResult::NO_NEXTHOP);
294
295 testRemoveNextHop(makeParameters("/hello", face2));
296 BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
297 BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
298 BOOST_CHECK_EQUAL(checkNextHop("/hello", 1, face2, 202), CheckNextHopResult::NO_NEXTHOP);
299
300 testRemoveNextHop(makeParameters("/hello", face3));
301 BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
302 BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
303 BOOST_CHECK_EQUAL(checkNextHop("/hello", 0, face3, 303), CheckNextHopResult::NO_FIB_ENTRY);
304}
305
306BOOST_AUTO_TEST_CASE(PrefixNotFound)
307{
308 FaceId addedFaceId = addFace();
309 BOOST_CHECK(addedFaceId != INVALID_FACEID);
310
311 auto parameters = makeParameters("hello", addedFaceId);
312 auto command = makeControlCommandRequest("/localhost/nfd/fib/remove-nexthop", parameters);
313 auto response = makeResponse(200, "Success", parameters);
314
315 receiveInterest(command);
316 BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
317 BOOST_CHECK_EQUAL(checkResponse(0, command->getName(), response), CheckResponseResult::OK);
318}
319
320BOOST_AUTO_TEST_CASE(ImplicitFaceId)
321{
322 auto face1 = addFace();
323 auto face2 = addFace();
324 BOOST_REQUIRE(face1 != INVALID_FACEID && face2 != INVALID_FACEID);
325
326 Name expectedName;
327 ControlResponse expectedResponse;
328 auto testWithImplicitFaceId = [&] (ControlParameters parameters, FaceId face) {
329 m_responses.clear();
330 auto command = makeControlCommandRequest("/localhost/nfd/fib/remove-nexthop", parameters,
331 [face] (shared_ptr<Interest> interest) {
332 interest->setIncomingFaceId(face);
333 });
334 expectedName = command->getName();
335 expectedResponse = makeResponse(200, "Success", parameters.setFaceId(face));
336 receiveInterest(command);
337 };
338
339 shared_ptr<fib::Entry> entry = m_fib.insert("/hello").first;
340 entry->addNextHop(m_faceTable.get(face1), 101);
341 entry->addNextHop(m_faceTable.get(face2), 202);
342
343 testWithImplicitFaceId(ControlParameters().setName("/hello").setFaceId(0), face1);
344 BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
345 BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
346 BOOST_CHECK_EQUAL(checkNextHop("/hello", 1, face1, 101), CheckNextHopResult::NO_NEXTHOP);
347
348 testWithImplicitFaceId(ControlParameters().setName("/hello"), face2);
349 BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
350 BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
351 BOOST_CHECK_EQUAL(checkNextHop("/hello", 0, face2, 202), CheckNextHopResult::NO_FIB_ENTRY);
352}
353
354BOOST_AUTO_TEST_CASE(RecordNotExist)
355{
356 auto face1 = addFace();
357 auto face2 = addFace();
358 BOOST_REQUIRE(face1 != INVALID_FACEID && face2 != INVALID_FACEID);
359
360 Name expectedName;
361 ControlResponse expectedResponse;
362 auto testRemoveNextHop = [&] (ControlParameters parameters) {
363 m_responses.clear();
364 auto command = makeControlCommandRequest("/localhost/nfd/fib/remove-nexthop", parameters);
365 expectedName = command->getName();
366 expectedResponse = makeResponse(200, "Success", parameters);
367 receiveInterest(command);
368 };
369
370 m_fib.insert("/hello").first->addNextHop(m_faceTable.get(face1), 101);
371
372 testRemoveNextHop(makeParameters("/hello", face2 + 100));
373 BOOST_REQUIRE_EQUAL(m_responses.size(), 1); // face does not exist
374 BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
375 BOOST_CHECK_EQUAL(checkNextHop("/hello", -1, face2 + 100), CheckNextHopResult::NO_NEXTHOP);
376
377 testRemoveNextHop(makeParameters("/hello", face2));
378 BOOST_REQUIRE_EQUAL(m_responses.size(), 1); // record does not exist
379 BOOST_CHECK_EQUAL(checkResponse(0, expectedName, expectedResponse), CheckResponseResult::OK);
380 BOOST_CHECK_EQUAL(checkNextHop("/hello", -1, face2), CheckNextHopResult::NO_NEXTHOP);
381}
382
383BOOST_AUTO_TEST_SUITE_END() // RemoveNextHop
384
385// @todo Remove when ndn::nfd::FibEntry implements operator!= and operator<<
386class FibEntry : public ndn::nfd::FibEntry
387{
388public:
389 FibEntry() = default;
390
391 FibEntry(const ndn::nfd::FibEntry& entry)
392 : ndn::nfd::FibEntry(entry)
393 {
394 }
395};
396
397bool
398operator!=(const FibEntry& left, const FibEntry& right)
399{
400 if (left.getPrefix() != right.getPrefix()) {
401 return true;
402 }
403
404 auto leftNextHops = left.getNextHopRecords();
405 auto rightNextHops = right.getNextHopRecords();
406 if (leftNextHops.size() != rightNextHops.size()) {
407 return true;
408 }
409
410 for (auto&& nexthop : leftNextHops) {
411 auto hitEntry =
412 std::find_if(rightNextHops.begin(), rightNextHops.end(), [&] (const ndn::nfd::NextHopRecord& record) {
413 return nexthop.getCost() == record.getCost() && nexthop.getFaceId() == record.getFaceId();
414 });
415
416 if (hitEntry == rightNextHops.end()) {
417 return true;
418 }
419 }
420
421 return false;
422}
423
424std::ostream&
425operator<<(std::ostream &os, const FibEntry& entry)
426{
427 const auto& nexthops = entry.getNextHopRecords();
428 os << "[" << entry.getPrefix() << ", " << nexthops.size() << ": ";
429 for (auto record : nexthops) {
430 os << "{" << record.getFaceId() << ", " << record.getCost() << "} ";
431 }
432 os << "]";
433
434 return os;
435}
436
437BOOST_AUTO_TEST_CASE(FibDataset)
438{
439 const size_t nEntries = 108;
440 std::set<Name> actualPrefixes;
441 for (size_t i = 0 ; i < nEntries ; i ++) {
442 Name prefix = Name("test").appendSegment(i);
443 actualPrefixes.insert(prefix);
444 auto fibEntry = m_fib.insert(prefix).first;
445 fibEntry->addNextHop(m_faceTable.get(addFace()), std::numeric_limits<uint8_t>::max() - 1);
446 fibEntry->addNextHop(m_faceTable.get(addFace()), std::numeric_limits<uint8_t>::max() - 2);
447 }
448
449 receiveInterest(makeInterest("/localhost/nfd/fib/list"));
450
451 Block content;
452 BOOST_CHECK_NO_THROW(content = concatenateResponses());
453 BOOST_CHECK_NO_THROW(content.parse());
454 BOOST_REQUIRE_EQUAL(content.elements().size(), nEntries);
455
456 std::vector<FibEntry> receivedRecords, expectedRecords;
457 for (size_t idx = 0; idx < nEntries; ++idx) {
458 BOOST_TEST_MESSAGE("processing element: " << idx);
459
460 FibEntry decodedEntry;
461 BOOST_REQUIRE_NO_THROW(decodedEntry.wireDecode(content.elements()[idx]));
462 receivedRecords.push_back(decodedEntry);
463
464 actualPrefixes.erase(decodedEntry.getPrefix());
465
466 auto matchedEntry = m_fib.findExactMatch(decodedEntry.getPrefix());
467 BOOST_REQUIRE(matchedEntry != nullptr);
468
469 FibEntry record;
470 record.setPrefix(matchedEntry->getPrefix());
471 const auto& nextHops = matchedEntry->getNextHops();
472 for (auto&& next : nextHops) {
473 ndn::nfd::NextHopRecord nextHopRecord;
474 nextHopRecord.setFaceId(next.getFace()->getId());
475 nextHopRecord.setCost(next.getCost());
476 record.addNextHopRecord(nextHopRecord);
477 }
478 expectedRecords.push_back(record);
479 }
480
481 BOOST_CHECK_EQUAL(actualPrefixes.size(), 0);
482
483 BOOST_CHECK_EQUAL_COLLECTIONS(receivedRecords.begin(), receivedRecords.end(),
484 expectedRecords.begin(), expectedRecords.end());
485}
486
487BOOST_AUTO_TEST_SUITE_END() // TestFibManager
488BOOST_AUTO_TEST_SUITE_END() // Mgmt
489
490} // namespace tests
491} // namespace nfd