blob: 8e4da1fc42587aedb70feea026b5ed1588f1bc14 [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
Yanbiao Li73860e32015-08-19 16:30:16 -0700304 std::set<FaceId> faceIds;
305 for (size_t idx = 0; idx < nEntries; ++idx) {
306 BOOST_TEST_MESSAGE("processing element: " << idx);
307
308 ndn::nfd::FaceStatus decodedStatus;
309 BOOST_REQUIRE_NO_THROW(decodedStatus.wireDecode(content.elements()[idx]));
Junxiao Shida93f1f2015-11-11 06:13:16 -0700310 BOOST_CHECK(m_faceTable.get(decodedStatus.getFaceId()) != nullptr);
Yanbiao Li73860e32015-08-19 16:30:16 -0700311 faceIds.insert(decodedStatus.getFaceId());
Yanbiao Li73860e32015-08-19 16:30:16 -0700312 }
313
314 BOOST_CHECK_EQUAL(faceIds.size(), nEntries);
Junxiao Shida93f1f2015-11-11 06:13:16 -0700315 // TODO#3325 check dataset contents including counter values
Yanbiao Li73860e32015-08-19 16:30:16 -0700316}
317
318BOOST_AUTO_TEST_CASE(FaceQuery)
319{
320 auto face1 = addFace<DummyFace>(true); // dummy://
321 auto face2 = addFace<DummyLocalFace>(true); // dummy://, local
322 auto face3 = addFace<TestFace>(true); // test://
323
324 auto generateQueryName = [] (const ndn::nfd::FaceQueryFilter& filter) {
325 return Name("/localhost/nfd/faces/query").append(filter.wireEncode());
326 };
327
328 auto querySchemeName =
329 generateQueryName(ndn::nfd::FaceQueryFilter().setUriScheme("dummy"));
330 auto queryIdName =
331 generateQueryName(ndn::nfd::FaceQueryFilter().setFaceId(face1->getId()));
332 auto queryScopeName =
333 generateQueryName(ndn::nfd::FaceQueryFilter().setFaceScope(ndn::nfd::FACE_SCOPE_NON_LOCAL));
334 auto invalidQueryName =
335 Name("/localhost/nfd/faces/query").append(ndn::makeStringBlock(tlv::Content, "invalid"));
336
337 receiveInterest(makeInterest(querySchemeName)); // face1 and face2 expected
338 receiveInterest(makeInterest(queryIdName)); // face1 expected
339 receiveInterest(makeInterest(queryScopeName)); // face1 and face3 expected
340 receiveInterest(makeInterest(invalidQueryName)); // nack expected
341
342 BOOST_REQUIRE_EQUAL(m_responses.size(), 4);
343
344 Block content;
345 ndn::nfd::FaceStatus status;
346
347 content = m_responses[0].getContent();
348 BOOST_CHECK_NO_THROW(content.parse());
349 BOOST_CHECK_EQUAL(content.elements().size(), 2); // face1 and face2
350 BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[0]));
351 BOOST_CHECK_EQUAL(face1->getId(), status.getFaceId());
352 BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[1]));
353 BOOST_CHECK_EQUAL(face2->getId(), status.getFaceId());
354
355 content = m_responses[1].getContent();
356 BOOST_CHECK_NO_THROW(content.parse());
357 BOOST_CHECK_EQUAL(content.elements().size(), 1); // face1
358 BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[0]));
359 BOOST_CHECK_EQUAL(face1->getId(), status.getFaceId());
360
361 content = m_responses[2].getContent();
362 BOOST_CHECK_NO_THROW(content.parse());
363 BOOST_CHECK_EQUAL(content.elements().size(), 2); // face1 and face3
364 BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[0]));
365 BOOST_CHECK_EQUAL(face1->getId(), status.getFaceId());
366 BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[1]));
367 BOOST_CHECK_EQUAL(face3->getId(), status.getFaceId());
368
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200369 ControlResponse expectedResponse(400, "Malformed filter"); // nack, 400, malformed filter
Yanbiao Li73860e32015-08-19 16:30:16 -0700370 BOOST_CHECK_EQUAL(checkResponse(3, invalidQueryName, expectedResponse, tlv::ContentType_Nack),
371 CheckResponseResult::OK);
372}
373
374class TestChannel : public Channel
375{
376public:
377 TestChannel(const std::string& uri)
378 {
379 setUri(FaceUri(uri));
380 }
381};
382
383class TestProtocolFactory : public ProtocolFactory
384{
385public:
386 virtual void
387 createFace(const FaceUri& uri,
388 ndn::nfd::FacePersistency persistency,
389 const FaceCreatedCallback& onCreated,
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200390 const FaceCreationFailedCallback& onConnectFailed) DECL_OVERRIDE
Yanbiao Li73860e32015-08-19 16:30:16 -0700391 {
392 }
393
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200394 virtual std::vector<shared_ptr<const Channel>>
Yanbiao Li73860e32015-08-19 16:30:16 -0700395 getChannels() const DECL_OVERRIDE
396 {
397 return m_channels;
398 }
399
400public:
401 shared_ptr<TestChannel>
402 addChannel(const std::string& channelUri)
403 {
404 auto channel = make_shared<TestChannel>(channelUri);
405 m_channels.push_back(channel);
406 return channel;
407 }
408
409private:
Davide Pesavento1d7e7af2015-10-10 23:54:08 +0200410 std::vector<shared_ptr<const Channel>> m_channels;
Yanbiao Li73860e32015-08-19 16:30:16 -0700411};
412
413BOOST_AUTO_TEST_CASE(ChannelDataset)
414{
415 auto factory = make_shared<TestProtocolFactory>();
416 m_manager.m_factories["test"] = factory;
417
418 std::map<std::string, shared_ptr<TestChannel>> addedChannels;
419 size_t nEntries = 404;
420 for (size_t i = 0 ; i < nEntries ; i ++) {
421 auto channel = factory->addChannel("test" + boost::lexical_cast<std::string>(i) + "://");
422 addedChannels[channel->getUri().toString()] = channel;
423 }
424
425 receiveInterest(makeInterest("/localhost/nfd/faces/channels"));
426
427 Block content;
428 BOOST_CHECK_NO_THROW(content = concatenateResponses());
429 BOOST_CHECK_NO_THROW(content.parse());
430 BOOST_REQUIRE_EQUAL(content.elements().size(), nEntries);
431
432 for (size_t idx = 0; idx < nEntries; ++idx) {
433 BOOST_TEST_MESSAGE("processing element: " << idx);
434
435 ndn::nfd::ChannelStatus decodedStatus;
436 BOOST_CHECK_NO_THROW(decodedStatus.wireDecode(content.elements()[idx]));
437 BOOST_CHECK(addedChannels.find(decodedStatus.getLocalUri()) != addedChannels.end());
438 }
439}
440
441BOOST_AUTO_TEST_SUITE_END() // TestFaceManager
442BOOST_AUTO_TEST_SUITE_END() // Mgmt
443
444} // namespace tests
445} // namespace nfd