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