blob: b8944b488e424f45850bff69ba2ca711ca9f78d8 [file] [log] [blame]
Yanbiao Li73860e32015-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
26#include "mgmt/face-manager.hpp"
27#include "manager-common-fixture.hpp"
28#include "../face/dummy-face.hpp"
29#include "face/tcp-factory.hpp"
30#include "face/udp-factory.hpp"
31
32#include <ndn-cxx/util/random.hpp>
33#include <ndn-cxx/encoding/tlv.hpp>
34#include <ndn-cxx/management/nfd-channel-status.hpp>
35#include <ndn-cxx/management/nfd-face-event-notification.hpp>
36
37namespace nfd {
38namespace tests {
39
40class FaceManagerFixture : public ManagerCommonFixture
41{
42public:
43 FaceManagerFixture()
44 : m_faceTable(m_forwarder.getFaceTable())
45 , m_manager(m_faceTable, m_dispatcher, m_validator)
46 {
47 setTopPrefixAndPrivilege("/localhost/nfd", "faces");
48 }
49
50public:
51 template<typename Face>
52 shared_ptr<Face>
53 addFace(bool wantRemoveLastNotification = false)
54 {
55 auto face = make_shared<Face>();
56 m_faceTable.add(face);
57 advanceClocks(time::milliseconds(1), 10); // wait for notification posted
58 if (wantRemoveLastNotification) {
59 m_responses.pop_back();
60 }
61 return face;
62 }
63
64protected:
65 FaceTable& m_faceTable;
66 FaceManager m_manager;
67};
68
69BOOST_FIXTURE_TEST_SUITE(Mgmt, FaceManagerFixture)
70BOOST_AUTO_TEST_SUITE(TestFaceManager)
71
72BOOST_AUTO_TEST_SUITE(DestroyFace)
73
74BOOST_AUTO_TEST_CASE(Existing)
75{
76 auto addedFace = addFace<DummyFace>(true); // clear notification for creation
77
78 auto parameters = ControlParameters().setFaceId(addedFace->getId());
79 auto command = makeControlCommandRequest("/localhost/nfd/faces/destroy", parameters);
80
81 receiveInterest(command);
82
83 BOOST_REQUIRE_EQUAL(m_responses.size(), 2); // one response and one notification
84 // notification is already tested, so ignore it
85
86 BOOST_CHECK_EQUAL(checkResponse(1, command->getName(), makeResponse(200, "OK", parameters)),
87 CheckResponseResult::OK);
88
89 BOOST_CHECK_EQUAL(addedFace->getId(), -1);
90}
91
92BOOST_AUTO_TEST_CASE(NonExisting)
93{
94 auto parameters = ControlParameters().setFaceId(65535);
95 auto command = makeControlCommandRequest("/localhost/nfd/faces/destroy", parameters);
96
97 receiveInterest(command);
98
99 BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
100
101 BOOST_CHECK_EQUAL(checkResponse(0, command->getName(), makeResponse(200, "OK", parameters)),
102 CheckResponseResult::OK);
103}
104
105BOOST_AUTO_TEST_SUITE_END() // DestroyFace
106
107BOOST_AUTO_TEST_CASE(FaceEvents)
108{
109 auto addedFace = addFace<DummyFace>(); // trigger FACE_EVENT_CREATED notification
110 BOOST_CHECK_NE(addedFace->getId(), -1);
111 int64_t faceId = addedFace->getId();
112
113 // check notification
114 {
115 Block payload;
116 ndn::nfd::FaceEventNotification notification;
117 BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
118 BOOST_CHECK_NO_THROW(payload = m_responses[0].getContent().blockFromValue());
119 BOOST_CHECK_EQUAL(payload.type(), ndn::tlv::nfd::FaceEventNotification);
120 BOOST_CHECK_NO_THROW(notification.wireDecode(payload));
121 BOOST_CHECK_EQUAL(notification.getKind(), ndn::nfd::FACE_EVENT_CREATED);
122 BOOST_CHECK_EQUAL(notification.getFaceId(), faceId);
123 BOOST_CHECK_EQUAL(notification.getRemoteUri(), addedFace->getRemoteUri().toString());
124 BOOST_CHECK_EQUAL(notification.getLocalUri(), addedFace->getLocalUri().toString());
125 BOOST_CHECK_EQUAL(notification.getFaceScope(), ndn::nfd::FACE_SCOPE_NON_LOCAL);
126 BOOST_CHECK_EQUAL(notification.getFacePersistency(), ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
127 BOOST_CHECK_EQUAL(notification.getLinkType(), ndn::nfd::LinkType::LINK_TYPE_POINT_TO_POINT);
128 }
129
130 addedFace->close(); // trigger FaceDestroy FACE_EVENT_DESTROYED
131 advanceClocks(time::milliseconds(1), 10);
132
133 // check notification
134 {
135 Block payload;
136 ndn::nfd::FaceEventNotification notification;
137 BOOST_REQUIRE_EQUAL(m_responses.size(), 2);
138 BOOST_CHECK_NO_THROW(payload = m_responses[1].getContent().blockFromValue());
139 BOOST_CHECK_EQUAL(payload.type(), ndn::tlv::nfd::FaceEventNotification);
140 BOOST_CHECK_NO_THROW(notification.wireDecode(payload));
141 BOOST_CHECK_EQUAL(notification.getKind(), ndn::nfd::FACE_EVENT_DESTROYED);
142 BOOST_CHECK_EQUAL(notification.getFaceId(), faceId);
143 BOOST_CHECK_EQUAL(notification.getRemoteUri(), addedFace->getRemoteUri().toString());
144 BOOST_CHECK_EQUAL(notification.getLocalUri(), addedFace->getLocalUri().toString());
145 BOOST_CHECK_EQUAL(notification.getFaceScope(), ndn::nfd::FACE_SCOPE_NON_LOCAL);
146 BOOST_CHECK_EQUAL(notification.getFacePersistency(), ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
147 BOOST_CHECK_EQUAL(notification.getLinkType(), ndn::nfd::LinkType::LINK_TYPE_POINT_TO_POINT);
148 }
149 BOOST_CHECK_EQUAL(addedFace->getId(), -1);
150}
151
152BOOST_AUTO_TEST_CASE(EnableDisableLocalControl)
153{
154 auto nonLocalFace = addFace<DummyFace>(true); // clear notification
155 auto localFace = addFace<DummyLocalFace>(true); // clear notification
156 BOOST_CHECK(localFace->isLocal());
157 BOOST_CHECK(!nonLocalFace->isLocal());
158
159 std::vector<Name> commandNames;
160 auto testLocalControl = [&] (const Name& name, const ControlParameters& params, uint64_t faceId) {
161 auto command = makeControlCommandRequest(name, params,
162 [faceId] (shared_ptr<Interest> interest) {
163 interest->setIncomingFaceId(faceId);
164 });
165 receiveInterest(command);
166 commandNames.push_back(command->getName());
167 };
168
169 auto paramsF = ControlParameters().setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
170 auto paramsN = ControlParameters().setLocalControlFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
171
172 // non-existing face: 0~3
173 testLocalControl("/localhost/nfd/faces/enable-local-control", paramsF, FACEID_NULL);
174 testLocalControl("/localhost/nfd/faces/disable-local-control", paramsF, FACEID_NULL);
175 testLocalControl("/localhost/nfd/faces/enable-local-control", paramsN, FACEID_NULL);
176 testLocalControl("/localhost/nfd/faces/disable-local-control", paramsN, FACEID_NULL);
177
178 // non-local face: 4~7
179 testLocalControl("/localhost/nfd/faces/enable-local-control", paramsF, nonLocalFace->getId());
180 testLocalControl("/localhost/nfd/faces/disable-local-control", paramsF, nonLocalFace->getId());
181 testLocalControl("/localhost/nfd/faces/enable-local-control", paramsN, nonLocalFace->getId());
182 testLocalControl("/localhost/nfd/faces/disable-local-control", paramsN, nonLocalFace->getId());
183
184 // enableLocalControl for Incoming FaceId on existing local face:
185 testLocalControl("/localhost/nfd/faces/enable-local-control", paramsF, localFace->getId()); // 8
186 BOOST_CHECK(localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
187 BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
188
189 // disableLocalControl for Incoming FaceId on existing local face
190 testLocalControl("/localhost/nfd/faces/disable-local-control", paramsF, localFace->getId()); // 9
191 BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
192 BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
193
194 // enableLocalControl for NextHop ID on existing local face
195 testLocalControl("/localhost/nfd/faces/enable-local-control", paramsN, localFace->getId()); // 10
196 BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
197 BOOST_CHECK(localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
198
199 // disableLocalControl for NextHop ID on existing local face
200 testLocalControl("/localhost/nfd/faces/disable-local-control", paramsN, localFace->getId()); // 11
201 BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
202 BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
203
204 // check responses
205 BOOST_REQUIRE_EQUAL(m_responses.size(), 12);
206 BOOST_CHECK_EQUAL(checkResponse(0, commandNames[0], ControlResponse(410, "Face not found")),
207 CheckResponseResult::OK);
208 BOOST_CHECK_EQUAL(checkResponse(1, commandNames[1], ControlResponse(410, "Face not found")),
209 CheckResponseResult::OK);
210 BOOST_CHECK_EQUAL(checkResponse(2, commandNames[2], ControlResponse(410, "Face not found")),
211 CheckResponseResult::OK);
212 BOOST_CHECK_EQUAL(checkResponse(3, commandNames[3], ControlResponse(410, "Face not found")),
213 CheckResponseResult::OK);
214 BOOST_CHECK_EQUAL(checkResponse(4, commandNames[4], ControlResponse(412, "Face is non-local")),
215 CheckResponseResult::OK);
216 BOOST_CHECK_EQUAL(checkResponse(5, commandNames[5], ControlResponse(412, "Face is non-local")),
217 CheckResponseResult::OK);
218 BOOST_CHECK_EQUAL(checkResponse(6, commandNames[6], ControlResponse(412, "Face is non-local")),
219 CheckResponseResult::OK);
220 BOOST_CHECK_EQUAL(checkResponse(7, commandNames[7], ControlResponse(412, "Face is non-local")),
221 CheckResponseResult::OK);
222 BOOST_CHECK_EQUAL(checkResponse(8, commandNames[8], makeResponse(200, "OK", paramsF)),
223 CheckResponseResult::OK);
224 BOOST_CHECK_EQUAL(checkResponse(9, commandNames[9], makeResponse(200, "OK", paramsF)),
225 CheckResponseResult::OK);
226 BOOST_CHECK_EQUAL(checkResponse(10, commandNames[10], makeResponse(200, "OK", paramsN)),
227 CheckResponseResult::OK);
228 BOOST_CHECK_EQUAL(checkResponse(11, commandNames[11], makeResponse(200, "OK", paramsN)),
229 CheckResponseResult::OK);
230}
231
232class TestFace : public DummyFace
233{
234public:
235 explicit
236 TestFace(const std::string& uri = "test://")
237 : DummyFace(uri, uri)
238 {
239 getMutableCounters().getNInInterests().set(ndn::random::generateWord64());
240 getMutableCounters().getNInDatas().set(ndn::random::generateWord64());
241 getMutableCounters().getNOutInterests().set(ndn::random::generateWord64());
242 getMutableCounters().getNOutDatas().set(ndn::random::generateWord64());
243 getMutableCounters().getNInBytes().set(ndn::random::generateWord64());
244 getMutableCounters().getNOutBytes().set(ndn::random::generateWord64());
245 }
246};
247
248// @todo Refactor when ndn::nfd::FaceStatus implementes operator!= and operator<<
249class FaceStatus : public ndn::nfd::FaceStatus
250{
251public:
252 FaceStatus(const ndn::nfd::FaceStatus& s)
253 : ndn::nfd::FaceStatus(s)
254 {
255 }
256};
257
258bool
259operator!=(const FaceStatus& left, const FaceStatus& right)
260{
261 return left.getRemoteUri() != right.getRemoteUri() ||
262 left.getLocalUri() != right.getLocalUri() ||
263 left.getFaceScope() != right.getFaceScope() ||
264 left.getFacePersistency() != right.getFacePersistency() ||
265 left.getLinkType() != right.getLinkType() ||
266 left.getNInInterests() != right.getNInInterests() ||
267 left.getNInDatas() != right.getNInDatas() ||
268 left.getNOutInterests() != right.getNOutInterests() ||
269 left.getNOutDatas() != right.getNOutDatas() ||
270 left.getNInBytes() != right.getNInBytes() ||
271 left.getNOutBytes() != right.getNOutBytes();
272}
273
274std::ostream&
275operator<<(std::ostream &os, const FaceStatus& status)
276{
277 os << "[" << status.getRemoteUri() << ", "
278 << status.getLocalUri() << ", "
279 << status.getFacePersistency() << ", "
280 << status.getLinkType() << ", "
281 << status.getNInInterests() << ", "
282 << status.getNInDatas() << ", "
283 << status.getNOutInterests() << ", "
284 << status.getNOutDatas() << ", "
285 << status.getNInBytes() << ", "
286 << status.getNOutBytes() << "]";
287 return os;
288}
289
290BOOST_AUTO_TEST_CASE(FaceDataset)
291{
292 size_t nEntries = 303;
293 for (size_t i = 0 ; i < nEntries ; i ++) {
294 addFace<TestFace>(true);
295 }
296
297 receiveInterest(makeInterest("/localhost/nfd/faces/list"));
298
299 Block content;
300 BOOST_CHECK_NO_THROW(content = concatenateResponses());
301 BOOST_CHECK_NO_THROW(content.parse());
302 BOOST_REQUIRE_EQUAL(content.elements().size(), nEntries);
303
304 std::vector<FaceStatus> expectedStatuses, receivedStatuses;
305 std::set<FaceId> faceIds;
306 for (size_t idx = 0; idx < nEntries; ++idx) {
307 BOOST_TEST_MESSAGE("processing element: " << idx);
308
309 ndn::nfd::FaceStatus decodedStatus;
310 BOOST_REQUIRE_NO_THROW(decodedStatus.wireDecode(content.elements()[idx]));
311 BOOST_REQUIRE(m_faceTable.get(decodedStatus.getFaceId()) != nullptr);
312 faceIds.insert(decodedStatus.getFaceId());
313 receivedStatuses.push_back(decodedStatus);
314 expectedStatuses.push_back(m_faceTable.get(decodedStatus.getFaceId())->getFaceStatus());
315 }
316
317 BOOST_CHECK_EQUAL(faceIds.size(), nEntries);
318 BOOST_CHECK_EQUAL_COLLECTIONS(receivedStatuses.begin(), receivedStatuses.end(),
319 expectedStatuses.begin(), expectedStatuses.end());
320}
321
322BOOST_AUTO_TEST_CASE(FaceQuery)
323{
324 auto face1 = addFace<DummyFace>(true); // dummy://
325 auto face2 = addFace<DummyLocalFace>(true); // dummy://, local
326 auto face3 = addFace<TestFace>(true); // test://
327
328 auto generateQueryName = [] (const ndn::nfd::FaceQueryFilter& filter) {
329 return Name("/localhost/nfd/faces/query").append(filter.wireEncode());
330 };
331
332 auto querySchemeName =
333 generateQueryName(ndn::nfd::FaceQueryFilter().setUriScheme("dummy"));
334 auto queryIdName =
335 generateQueryName(ndn::nfd::FaceQueryFilter().setFaceId(face1->getId()));
336 auto queryScopeName =
337 generateQueryName(ndn::nfd::FaceQueryFilter().setFaceScope(ndn::nfd::FACE_SCOPE_NON_LOCAL));
338 auto invalidQueryName =
339 Name("/localhost/nfd/faces/query").append(ndn::makeStringBlock(tlv::Content, "invalid"));
340
341 receiveInterest(makeInterest(querySchemeName)); // face1 and face2 expected
342 receiveInterest(makeInterest(queryIdName)); // face1 expected
343 receiveInterest(makeInterest(queryScopeName)); // face1 and face3 expected
344 receiveInterest(makeInterest(invalidQueryName)); // nack expected
345
346 BOOST_REQUIRE_EQUAL(m_responses.size(), 4);
347
348 Block content;
349 ndn::nfd::FaceStatus status;
350
351 content = m_responses[0].getContent();
352 BOOST_CHECK_NO_THROW(content.parse());
353 BOOST_CHECK_EQUAL(content.elements().size(), 2); // face1 and face2
354 BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[0]));
355 BOOST_CHECK_EQUAL(face1->getId(), status.getFaceId());
356 BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[1]));
357 BOOST_CHECK_EQUAL(face2->getId(), status.getFaceId());
358
359 content = m_responses[1].getContent();
360 BOOST_CHECK_NO_THROW(content.parse());
361 BOOST_CHECK_EQUAL(content.elements().size(), 1); // face1
362 BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[0]));
363 BOOST_CHECK_EQUAL(face1->getId(), status.getFaceId());
364
365 content = m_responses[2].getContent();
366 BOOST_CHECK_NO_THROW(content.parse());
367 BOOST_CHECK_EQUAL(content.elements().size(), 2); // face1 and face3
368 BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[0]));
369 BOOST_CHECK_EQUAL(face1->getId(), status.getFaceId());
370 BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[1]));
371 BOOST_CHECK_EQUAL(face3->getId(), status.getFaceId());
372
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200373 ControlResponse expectedResponse(400, "Malformed filter"); // nack, 400, malformed filter
Yanbiao Li73860e32015-08-19 16:30:16 -0700374 BOOST_CHECK_EQUAL(checkResponse(3, invalidQueryName, expectedResponse, tlv::ContentType_Nack),
375 CheckResponseResult::OK);
376}
377
378class TestChannel : public Channel
379{
380public:
381 TestChannel(const std::string& uri)
382 {
383 setUri(FaceUri(uri));
384 }
385};
386
387class TestProtocolFactory : public ProtocolFactory
388{
389public:
390 virtual void
391 createFace(const FaceUri& uri,
392 ndn::nfd::FacePersistency persistency,
393 const FaceCreatedCallback& onCreated,
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200394 const FaceCreationFailedCallback& onConnectFailed) DECL_OVERRIDE
Yanbiao Li73860e32015-08-19 16:30:16 -0700395 {
396 }
397
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200398 virtual std::vector<shared_ptr<const Channel>>
Yanbiao Li73860e32015-08-19 16:30:16 -0700399 getChannels() const DECL_OVERRIDE
400 {
401 return m_channels;
402 }
403
404public:
405 shared_ptr<TestChannel>
406 addChannel(const std::string& channelUri)
407 {
408 auto channel = make_shared<TestChannel>(channelUri);
409 m_channels.push_back(channel);
410 return channel;
411 }
412
413private:
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200414 std::vector<shared_ptr<const Channel>> m_channels;
Yanbiao Li73860e32015-08-19 16:30:16 -0700415};
416
417BOOST_AUTO_TEST_CASE(ChannelDataset)
418{
419 auto factory = make_shared<TestProtocolFactory>();
420 m_manager.m_factories["test"] = factory;
421
422 std::map<std::string, shared_ptr<TestChannel>> addedChannels;
423 size_t nEntries = 404;
424 for (size_t i = 0 ; i < nEntries ; i ++) {
425 auto channel = factory->addChannel("test" + boost::lexical_cast<std::string>(i) + "://");
426 addedChannels[channel->getUri().toString()] = channel;
427 }
428
429 receiveInterest(makeInterest("/localhost/nfd/faces/channels"));
430
431 Block content;
432 BOOST_CHECK_NO_THROW(content = concatenateResponses());
433 BOOST_CHECK_NO_THROW(content.parse());
434 BOOST_REQUIRE_EQUAL(content.elements().size(), nEntries);
435
436 for (size_t idx = 0; idx < nEntries; ++idx) {
437 BOOST_TEST_MESSAGE("processing element: " << idx);
438
439 ndn::nfd::ChannelStatus decodedStatus;
440 BOOST_CHECK_NO_THROW(decodedStatus.wireDecode(content.elements()[idx]));
441 BOOST_CHECK(addedChannels.find(decodedStatus.getLocalUri()) != addedChannels.end());
442 }
443}
444
445BOOST_AUTO_TEST_SUITE_END() // TestFaceManager
446BOOST_AUTO_TEST_SUITE_END() // Mgmt
447
448} // namespace tests
449} // namespace nfd