blob: f343738ecda9851e6fcd81381fbdd54844ca187b [file] [log] [blame]
Junxiao Shi80ee7cb2014-12-14 10:53:05 -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/** \file
27 * \brief allows testing forwarding in a network topology
28 */
29
30#ifndef NFD_TESTS_NFD_FW_TOPOLOGY_TESTER_HPP
31#define NFD_TESTS_NFD_FW_TOPOLOGY_TESTER_HPP
32
33#include <unordered_map>
34#include <ndn-cxx/util/dummy-client-face.hpp>
35#include "fw/strategy.hpp"
36#include "tests/test-common.hpp"
37#include "../face/dummy-face.hpp"
38
39namespace nfd {
40namespace tests {
41
42using ndn::util::DummyClientFace;
43
44/** \brief identifies a node (forwarder) in the topology
45 */
46typedef size_t TopologyNode;
47
48/** \brief represents a network or local-app link
49 */
50class TopologyLinkBase : noncopyable
51{
52public:
53 TopologyLinkBase()
54 : m_isUp(true)
55 {
56 }
57
58 /** \brief fail the link, cause packets to be dropped silently
59 */
60 void
61 fail()
62 {
63 m_isUp = false;
64 }
65
66 /** \brief recover the link from a failure
67 */
68 void
69 recover()
70 {
71 m_isUp = true;
72 }
73
74protected:
75 bool m_isUp;
76};
77
78/** \brief represents a network link in the topology which connects two or more nodes
79 */
80class TopologyLink : public TopologyLinkBase
81{
82public:
83 /** \return a face of forwarder \p i which is attached to this link
84 */
85 shared_ptr<DummyFace>
86 getFace(TopologyNode i)
87 {
88 return m_faces.at(i)->face;
89 }
90
91private:
92 explicit
93 TopologyLink(const time::nanoseconds& delay)
94 : m_delay(delay)
95 {
96 BOOST_ASSERT(delay >= time::nanoseconds::zero());
97 }
98
99 struct LinkFace
100 {
101 shared_ptr<DummyFace> face;
102 };
103
104 void
105 addFace(TopologyNode i, shared_ptr<DummyFace> face)
106 {
107 BOOST_ASSERT(m_faces.count(i) == 0);
108
109 LinkFace* lf = new LinkFace();
110 lf->face = face;
111 face->onSendInterest.connect(bind(&TopologyLink::transmitInterest, this, i, _1));
112 face->onSendData.connect(bind(&TopologyLink::transmitData, this, i, _1));
113
114 m_faces[i].reset(lf);
115 }
116
117 friend class TopologyTester;
118
119private:
120 void
121 transmitInterest(TopologyNode i, const Interest& interest)
122 {
123 if (!m_isUp) {
124 return;
125 }
126
127 // Interest object cannot be shared between faces because
128 // Forwarder can set different IncomingFaceId.
129 Block wire = interest.wireEncode();
130 for (auto&& p : m_faces) {
131 if (p.first == i) {
132 continue;
133 }
134 shared_ptr<DummyFace> face = p.second->face;
135 scheduler::schedule(m_delay, [wire, face] {
136 auto interest = make_shared<Interest>(wire);
137 face->receiveInterest(*interest);
138 });
139 }
140 }
141
142 void
143 transmitData(TopologyNode i, const Data& data)
144 {
145 if (!m_isUp) {
146 return;
147 }
148
149 // Data object cannot be shared between faces because
150 // Forwarder can set different IncomingFaceId.
151 Block wire = data.wireEncode();
152 for (auto&& p : m_faces) {
153 if (p.first == i) {
154 continue;
155 }
156 shared_ptr<DummyFace> face = p.second->face;
157 scheduler::schedule(m_delay, [wire, face] {
158 auto data = make_shared<Data>(wire);
159 face->receiveData(*data);
160 });
161 }
162 }
163
164private:
165 time::nanoseconds m_delay;
166 std::unordered_map<TopologyNode, unique_ptr<LinkFace>> m_faces;
167};
168
169/** \brief represents a link to a local application
170 */
171class TopologyAppLink : public TopologyLinkBase
172{
173public:
174 /** \return face on forwarder side
175 */
176 shared_ptr<DummyLocalFace>
177 getForwarderFace()
178 {
179 return m_face;
180 }
181
182 /** \return face on application side
183 */
184 shared_ptr<DummyClientFace>
185 getClientFace()
186 {
187 return m_client;
188 }
189
190private:
191 explicit
192 TopologyAppLink(shared_ptr<DummyLocalFace> face)
193 : m_face(face)
194 , m_client(ndn::util::makeDummyClientFace(getGlobalIoService(), {false, false}))
195 {
196 m_client->onSendInterest.connect([this] (const Interest& interest) {
197 if (!m_isUp) {
198 return;
199 }
200 auto interest2 = interest.shared_from_this();
201 getGlobalIoService().post([=] { m_face->receiveInterest(*interest2); });
202 });
203
204 m_client->onSendData.connect([this] (const Data& data) {
205 if (!m_isUp) {
206 return;
207 }
208 auto data2 = data.shared_from_this();
209 getGlobalIoService().post([=] { m_face->receiveData(*data2); });
210 });
211
212 m_face->onSendInterest.connect([this] (const Interest& interest) {
213 if (!m_isUp) {
214 return;
215 }
216 auto interest2 = interest.shared_from_this();
217 getGlobalIoService().post([=] { m_client->receive(*interest2); });
218 });
219
220 m_face->onSendData.connect([this] (const Data& data) {
221 if (!m_isUp) {
222 return;
223 }
224 auto data2 = data.shared_from_this();
225 getGlobalIoService().post([=] { m_client->receive(*data2); });
226 });
227 }
228
229 friend class TopologyTester;
230
231private:
232 shared_ptr<DummyLocalFace> m_face;
233 shared_ptr<DummyClientFace> m_client;
234};
235
236/** \brief builds a topology for forwarding tests
237 */
238class TopologyTester : noncopyable
239{
240public:
241 /** \brief creates a forwarder
242 * \return index of new forwarder
243 */
244 TopologyNode
245 addForwarder()
246 {
247 size_t i = m_forwarders.size();
248 m_forwarders.push_back(std::move(unique_ptr<Forwarder>(new Forwarder())));
249 return i;
250 }
251
252 /** \return forwarder instance \p i
253 */
254 Forwarder&
255 getForwarder(TopologyNode i)
256 {
257 return *m_forwarders.at(i);
258 }
259
260 /** \brief sets strategy on forwarder \p i
261 * \tparam the strategy type
262 * \note Test scenario can also access StrategyChoice table directly.
263 */
264 template<typename S>
265 void
266 setStrategy(TopologyNode i, Name prefix = Name("ndn:/"))
267 {
268 Forwarder& forwarder = this->getForwarder(i);
269 StrategyChoice& strategyChoice = forwarder.getStrategyChoice();
270 shared_ptr<S> strategy = make_shared<S>(ref(forwarder));
271 strategyChoice.install(strategy);
272 strategyChoice.insert(prefix, strategy->getName());
273 }
274
275 /** \brief makes a link that interconnects two or more forwarders
276 *
277 * A face is created on each of \p forwarders .
278 * When a packet is sent onto one of the faces on this link,
279 * this packet will be received by all other faces on this link after \p delay .
280 */
281 shared_ptr<TopologyLink>
282 addLink(const time::nanoseconds& delay, std::initializer_list<TopologyNode> forwarders)
283 {
284 auto link = shared_ptr<TopologyLink>(new TopologyLink(delay));
285 for (TopologyNode i : forwarders) {
286 Forwarder& forwarder = this->getForwarder(i);
287 shared_ptr<DummyFace> face = make_shared<DummyFace>();
288 forwarder.addFace(face);
289 link->addFace(i, face);
290 }
291 return link;
292 }
293
294 /** \brief makes a link to local application
295 */
296 shared_ptr<TopologyAppLink>
297 addAppFace(TopologyNode i)
298 {
299 Forwarder& forwarder = this->getForwarder(i);
300 auto face = make_shared<DummyLocalFace>();
301 forwarder.addFace(face);
302
303 return shared_ptr<TopologyAppLink>(new TopologyAppLink(face));
304 }
305
306 /** \brief makes a link to local application, and register a prefix
307 */
308 shared_ptr<TopologyAppLink>
309 addAppFace(TopologyNode i, const Name& prefix, uint64_t cost = 0)
310 {
311 shared_ptr<TopologyAppLink> al = this->addAppFace(i);
312 this->registerPrefix(i, al->getForwarderFace(), prefix, cost);
313 return al;
314 }
315
316 /** \brief registers a prefix on a face
317 * \tparam F either DummyFace or DummyLocalFace
318 */
319 template<typename F>
320 void
321 registerPrefix(TopologyNode i, shared_ptr<F> face, const Name& prefix, uint64_t cost = 0)
322 {
323 Forwarder& forwarder = this->getForwarder(i);
324 Fib& fib = forwarder.getFib();
325 shared_ptr<fib::Entry> fibEntry = fib.insert(prefix).first;
326 fibEntry->addNextHop(face, cost);
327 }
328
329 /** \brief creates a producer application that answers every Interest with Data of same Name
330 */
331 void
332 addEchoProducer(DummyClientFace& face, const Name& prefix = "/")
333 {
334 face.setInterestFilter(prefix,
335 [&face] (const ndn::InterestFilter&, const Interest& interest) {
336 shared_ptr<Data> data = makeData(interest.getName());
337 face.put(*data);
338 });
339 }
340
341 /** \brief creates a consumer application that sends \p n Interests under \p prefix
342 * at \p interval fixed rate.
343 */
344 void
345 addIntervalConsumer(DummyClientFace& face, const Name& prefix,
346 const time::nanoseconds& interval, size_t n)
347 {
348 Name name(prefix);
349 name.appendTimestamp();
350 shared_ptr<Interest> interest = makeInterest(name);
351 face.expressInterest(*interest, bind([]{}));
352
353 if (n > 1) {
354 scheduler::schedule(interval, bind(&TopologyTester::addIntervalConsumer, this,
355 ref(face), prefix, interval, n - 1));
356 }
357 }
358
359private:
360 std::vector<unique_ptr<Forwarder>> m_forwarders;
361};
362
363} // namespace tests
364} // namespace nfd
365
366#endif // NFD_TESTS_NFD_FW_TOPOLOGY_TESTER_HPP