blob: 836c3799e27886e46130e1a5f48df1e8a117e7ad [file] [log] [blame]
Alexander Afanasyev45b92d42011-08-14 23:11:38 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2011 UCLA
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation;
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Author:
19 */
20
21/**
22 * \ingroup ccnx
23 * \defgroup CcnxStackModel Ccnx Stack Model
24 *
25 * \section CcnxStackTracingModel Tracing in the Ccnx Stack
26 *
27 * The ccnx stack provides a number of trace sources in its various
28 * protocol implementations. These trace sources can be hooked using your own
29 * custom trace code, or you can use our helper functions in some cases to
30 * arrange for tracing to be enabled.
31 *
32 * \subsection CcnxStackCcnxTracingModel Tracing in Ccnx
33 *
34 * The Ccnx layer three protocol provides three trace hooks. These are the
35 * "Tx" (ns3::CcnxL3Protocol::m_txTrace), "Rx" (ns3::CcnxL3Protocol::m_rxTrace)
36 * and "Drop" (ns3::CcnxL3Protocol::m_dropTrace) trace sources.
37 *
38 * The "Tx" trace is fired in a number of situations, all of which indicate that
39 * a given packet is about to be sent down to a given ns3::CcnxFace.
40 *
41 * - \todo list Tx trace events
42 *
43 * The "Rx" trace is fired when a packet is passed from the device up to the
44 * ns3::CcnxL3Protocol::Receive function.
45 *
46 * - In the receive function, the CcnxFaceList is iterated, and if the
47 * CcnxFace corresponding to the receiving device is found to be in the
48 * UP state, the trace is fired.
49 *
50 * The "Drop" trace is fired in any case where the packet is dropped (in both
51 * the transmit and receive paths).
52 *
53 * - \todo list Drop trace events
54 */
55
56#include "ns3/assert.h"
57#include "ns3/log.h"
58#include "ns3/object.h"
59#include "ns3/names.h"
60#include "ns3/ccnx.h"
61#include "ns3/packet-socket-factory.h"
62#include "ns3/config.h"
63#include "ns3/simulator.h"
64#include "ns3/string.h"
65#include "ns3/net-device.h"
66#include "ns3/callback.h"
67#include "ns3/node.h"
68#include "ns3/core-config.h"
69#include "ns3/ccnx-forwarding-protocol.h"
70
71#include "ccnx-stack-helper.h"
72#include "ccnx-forwarding-helper.h"
73
74#include <limits>
75#include <map>
76
77NS_LOG_COMPONENT_DEFINE ("CcnxStackHelper");
78
79namespace ns3 {
80
81// Things are going to work differently here with respect to trace
82// file handling than in most places because the Tx and Rx trace
83// sources we are interested in are going to multiplex receive and
84// transmit callbacks for all Ccnx and face pairs through one
85// callback. We want packets to or from each distinct pair to go to
86// an individual file, so we have got to demultiplex the Ccnx and face
87// pair into a corresponding Ptr<PcapFileWrapper> at the callback.
88//
89// A complication in this situation is that the trace sources are
90// hooked on a protocol basis. There is no trace source hooked by an
91// Ccnx and face pair. This means that if we naively proceed to hook,
92// say, a drop trace for a given Ccnx with face 0, and then hook for
93// Ccnx with face 1 we will hook the drop trace twice and get two
94// callbacks per event. What we need to do is to hook the event once,
95// and that will result in a single callback per drop event, and the
96// trace source will provide the face which we filter on in the trace
97// sink.
98//
99// This has got to continue to work properly after the helper has been
100// destroyed; but must be cleaned up at the end of time to avoid
101// leaks. Global maps of protocol/face pairs to file objects seems to
102// fit the bill.
103//
104typedef std::pair<Ptr<Ccnx>, uint32_t> FacePairCcnx;
105typedef std::map<FacePairCcnx, Ptr<PcapFileWrapper> > FaceFileMapCcnx;
106typedef std::map<FacePairCcnx, Ptr<OutputStreamWrapper> > FaceStreamMapCcnx;
107
108static FaceFileMapCcnx g_faceFileMapCcnx; /**< A mapping of Ccnx/face pairs to pcap files */
109static FaceStreamMapCcnx g_faceStreamMapCcnx; /**< A mapping of Ccnx/face pairs to ascii streams */
110
111CcnxStackHelper::CcnxStackHelper ()
112 : m_forwarding (0)
113 , m_ccnxEnabled (true)
114{
115 Initialize ();
116}
117
118// private method called by both constructor and Reset ()
119void
120CcnxStackHelper::Initialize ()
121{
122 // CcnxStaticForwardingHelper staticForwarding;
123 // CcnxGlobalForwardingHelper globalForwarding;
124 // CcnxListForwardingHelper listForwarding;
125 // listForwarding.Add (staticForwarding, 0);
126 // listForwarding.Add (globalForwarding, -10);
127 // SetForwardingHelper (listForwarding);
128}
129
130CcnxStackHelper::~CcnxStackHelper ()
131{
132 if (m_forwarding)
133 {
134 delete m_forwarding;
135 m_forwarding = 0;
136 }
137}
138
139CcnxStackHelper::CcnxStackHelper (const CcnxStackHelper &o)
140{
141 m_forwarding = o.m_forwarding->Copy ();
142 m_ccnxEnabled = o.m_ccnxEnabled;
143}
144
145CcnxStackHelper &
146CcnxStackHelper::operator = (const CcnxStackHelper &o)
147{
148 if (this == &o)
149 {
150 return *this;
151 }
152 m_forwarding = o.m_forwarding->Copy ();
153 return *this;
154}
155
156void
157CcnxStackHelper::Reset (void)
158{
159 delete m_forwarding;
160 m_forwarding = 0;
161 m_ccnxEnabled = true;
162 Initialize ();
163}
164
165void
166CcnxStackHelper::SetForwardingHelper (const CcnxForwardingHelper &forwarding)
167{
168 delete m_forwarding;
169 m_forwarding = forwarding.Copy ();
170}
171
172void
173CcnxStackHelper::SetCcnxStackInstall (bool enable)
174{
175 m_ccnxEnabled = enable;
176}
177
178void
179CcnxStackHelper::Install (NodeContainer c) const
180{
181 for (NodeContainer::Iterator i = c.Begin (); i != c.End (); ++i)
182 {
183 Install (*i);
184 }
185}
186
187void
188CcnxStackHelper::InstallAll (void) const
189{
190 Install (NodeContainer::GetGlobal ());
191}
192
193void
194CcnxStackHelper::CreateAndAggregateObjectFromTypeId (Ptr<Node> node, const std::string typeId)
195{
196 ObjectFactory factory;
197 factory.SetTypeId (typeId);
198 Ptr<Object> protocol = factory.Create <Object> ();
199 node->AggregateObject (protocol);
200}
201
202void
203CcnxStackHelper::Install (Ptr<Node> node) const
204{
205 NS_ASSERT_MSG (m_forwarding, "CcnxForwarding should be set prior calling Install() method");
206
207 if (m_ccnxEnabled)
208 {
209 if (node->GetObject<Ccnx> () != 0)
210 {
211 NS_FATAL_ERROR ("CcnxStackHelper::Install (): Installing "
212 "a CcnxStack to a node with an existing Ccnx object");
213 return;
214 }
215
216 CreateAndAggregateObjectFromTypeId (node, "ns3::CcnxL3Protocol");
217 // Set forwarding
218 Ptr<Ccnx> ccnx = node->GetObject<Ccnx> ();
219 Ptr<CcnxForwardingProtocol> ccnxForwarding = m_forwarding->Create (node);
220 ccnx->SetForwardingProtocol (ccnxForwarding);
221 }
222}
223
224void
225CcnxStackHelper::Install (std::string nodeName) const
226{
227 Ptr<Node> node = Names::Find<Node> (nodeName);
228 Install (node);
229}
230
231static void
232CcnxL3ProtocolRxTxSink (Ptr<const Packet> p, Ptr<Ccnx> ccnx, uint32_t face)
233{
234 NS_LOG_FUNCTION (p << ccnx << face);
235
236 //
237 // Since trace sources are independent of face, if we hook a source
238 // on a particular protocol we will get traces for all of its faces.
239 // We need to filter this to only report faces for which the user
240 // has expressed interest.
241 //
242 FacePairCcnx pair = std::make_pair (ccnx, face);
243 if (g_faceFileMapCcnx.find (pair) == g_faceFileMapCcnx.end ())
244 {
245 NS_LOG_INFO ("Ignoring packet to/from face " << face);
246 return;
247 }
248
249 Ptr<PcapFileWrapper> file = g_faceFileMapCcnx[pair];
250 file->Write (Simulator::Now (), p);
251}
252
253bool
254CcnxStackHelper::PcapHooked (Ptr<Ccnx> ccnx)
255{
256 for (FaceFileMapCcnx::const_iterator i = g_faceFileMapCcnx.begin ();
257 i != g_faceFileMapCcnx.end ();
258 ++i)
259 {
260 if ((*i).first.first == ccnx)
261 {
262 return true;
263 }
264 }
265 return false;
266}
267
268void
269CcnxStackHelper::EnablePcapCcnxInternal (std::string prefix, Ptr<Ccnx> ccnx, uint32_t face, bool explicitFilename)
270{
271 NS_LOG_FUNCTION (prefix << ccnx << face);
272
273 if (!m_ccnxEnabled)
274 {
275 NS_LOG_INFO ("Call to enable Ccnx pcap tracing but Ccnx not enabled");
276 return;
277 }
278
279 //
280 // We have to create a file and a mapping from protocol/face to file
281 // irrespective of how many times we want to trace a particular protocol.
282 //
283 PcapHelper pcapHelper;
284
285 std::string filename;
286 if (explicitFilename)
287 {
288 filename = prefix;
289 }
290 else
291 {
292 filename = pcapHelper.GetFilenameFromInterfacePair (prefix, ccnx, face);
293 }
294
295 Ptr<PcapFileWrapper> file = pcapHelper.CreateFile (filename, std::ios::out, PcapHelper::DLT_RAW);
296
297 //
298 // However, we only hook the trace source once to avoid multiple trace sink
299 // calls per event (connect is independent of face).
300 //
301 if (!PcapHooked (ccnx))
302 {
303 //
304 // Ptr<Ccnx> is aggregated to node and CcnxL3Protocol is aggregated to
305 // node so we can get to CcnxL3Protocol through Ccnx.
306 //
307 Ptr<CcnxL3Protocol> ccnxL3Protocol = ccnx->GetObject<CcnxL3Protocol> ();
308 NS_ASSERT_MSG (ccnxL3Protocol, "CcnxStackHelper::EnablePcapCcnxInternal(): "
309 "m_ccnxEnabled and ccnxL3Protocol inconsistent");
310
311 bool result = ccnxL3Protocol->TraceConnectWithoutContext ("Tx", MakeCallback (&CcnxL3ProtocolRxTxSink));
312 NS_ASSERT_MSG (result == true, "CcnxStackHelper::EnablePcapCcnxInternal(): "
313 "Unable to connect ccnxL3Protocol \"Tx\"");
314
315 result = ccnxL3Protocol->TraceConnectWithoutContext ("Rx", MakeCallback (&CcnxL3ProtocolRxTxSink));
316 NS_ASSERT_MSG (result == true, "CcnxStackHelper::EnablePcapCcnxInternal(): "
317 "Unable to connect ccnxL3Protocol \"Rx\"");
318 // cast result to void, to suppress ‘result’ set but not used compiler-warning
319 // for optimized builds
320 (void) result;
321 }
322
323 g_faceFileMapCcnx[std::make_pair (ccnx, face)] = file;
324}
325
326static void
327CcnxL3ProtocolDropSinkWithoutContext (
328 Ptr<OutputStreamWrapper> stream,
329 Ptr<const Packet> packet,
330 CcnxL3Protocol::DropReason reason,
331 Ptr<Ccnx> ccnx,
332 uint32_t face)
333{
334 //
335 // Since trace sources are independent of face, if we hook a source
336 // on a particular protocol we will get traces for all of its faces.
337 // We need to filter this to only report faces for which the user
338 // has expressed interest.
339 //
340 FacePairCcnx pair = std::make_pair (ccnx, face);
341 if (g_faceStreamMapCcnx.find (pair) == g_faceStreamMapCcnx.end ())
342 {
343 NS_LOG_INFO ("Ignoring packet to/from face " << face);
344 return;
345 }
346
347 *stream->GetStream () << "d " << Simulator::Now ().GetSeconds () << " " << *packet << std::endl;
348}
349
350static void
351CcnxL3ProtocolDropSinkWithContext (
352 Ptr<OutputStreamWrapper> stream,
353 std::string context,
354 Ptr<const Packet> packet,
355 CcnxL3Protocol::DropReason reason,
356 Ptr<Ccnx> ccnx,
357 uint32_t face)
358{
359 //
360 // Since trace sources are independent of face, if we hook a source
361 // on a particular protocol we will get traces for all of its faces.
362 // We need to filter this to only report faces for which the user
363 // has expressed interest.
364 //
365 FacePairCcnx pair = std::make_pair (ccnx, face);
366 if (g_faceStreamMapCcnx.find (pair) == g_faceStreamMapCcnx.end ())
367 {
368 NS_LOG_INFO ("Ignoring packet to/from face " << face);
369 return;
370 }
371
372 *stream->GetStream () << "d " << Simulator::Now ().GetSeconds () << " " << context << "(" << face << ") "
373 << *packet << std::endl;
374}
375
376bool
377CcnxStackHelper::AsciiHooked (Ptr<Ccnx> ccnx)
378{
379 for ( FaceStreamMapCcnx::const_iterator i = g_faceStreamMapCcnx.begin ();
380 i != g_faceStreamMapCcnx.end ();
381 ++i)
382 {
383 if ((*i).first.first == ccnx)
384 {
385 return true;
386 }
387 }
388 return false;
389}
390
391void
392CcnxStackHelper::EnableAsciiCcnxInternal (
393 Ptr<OutputStreamWrapper> stream,
394 std::string prefix,
395 Ptr<Ccnx> ccnx,
396 uint32_t face,
397 bool explicitFilename)
398{
399 if (!m_ccnxEnabled)
400 {
401 NS_LOG_INFO ("Call to enable Ccnx ascii tracing but Ccnx not enabled");
402 return;
403 }
404
405 //
406 // Our trace sinks are going to use packet printing, so we have to
407 // make sure that is turned on.
408 //
409 Packet::EnablePrinting ();
410
411 //
412 // If we are not provided an OutputStreamWrapper, we are expected to create
413 // one using the usual trace filename conventions and hook WithoutContext
414 // since there will be one file per context and therefore the context would
415 // be redundant.
416 //
417 if (stream == 0)
418 {
419 //
420 // Set up an output stream object to deal with private ofstream copy
421 // constructor and lifetime issues. Let the helper decide the actual
422 // name of the file given the prefix.
423 //
424 // We have to create a stream and a mapping from protocol/face to
425 // stream irrespective of how many times we want to trace a particular
426 // protocol.
427 //
428 AsciiTraceHelper asciiTraceHelper;
429
430 std::string filename;
431 if (explicitFilename)
432 {
433 filename = prefix;
434 }
435 else
436 {
437 filename = asciiTraceHelper.GetFilenameFromInterfacePair (prefix, ccnx, face);
438 }
439
440 Ptr<OutputStreamWrapper> theStream = asciiTraceHelper.CreateFileStream (filename);
441
442 //
443 // However, we only hook the trace sources once to avoid multiple trace sink
444 // calls per event (connect is independent of face).
445 //
446 if (!AsciiHooked (ccnx))
447 {
448 //
449 // The drop sink for the CcnxL3Protocol uses a different signature than
450 // the default sink, so we have to cook one up for ourselves. We can get
451 // to the Ptr<CcnxL3Protocol> through our Ptr<Ccnx> since they must both
452 // be aggregated to the same node.
453 //
454 Ptr<CcnxL3Protocol> ccnxL3Protocol = ccnx->GetObject<CcnxL3Protocol> ();
455 bool __attribute__ ((unused)) result = ccnxL3Protocol->TraceConnectWithoutContext ("Drop",
456 MakeBoundCallback (&CcnxL3ProtocolDropSinkWithoutContext, theStream));
457 NS_ASSERT_MSG (result == true, "CcnxStackHelper::EanableAsciiCcnxInternal(): "
458 "Unable to connect ccnxL3Protocol \"Drop\"");
459 }
460
461 g_faceStreamMapCcnx[std::make_pair (ccnx, face)] = theStream;
462 return;
463 }
464
465 //
466 // If we are provided an OutputStreamWrapper, we are expected to use it, and
467 // to provide a context. We are free to come up with our own context if we
468 // want, and use the AsciiTraceHelper Hook*WithContext functions, but for
469 // compatibility and simplicity, we just use Config::Connect and let it deal
470 // with the context.
471 //
472 // We need to associate the ccnx/face with a stream to express interest
473 // in tracing events on that pair, however, we only hook the trace sources
474 // once to avoid multiple trace sink calls per event (connect is independent
475 // of face).
476 //
477 if (!AsciiHooked (ccnx))
478 {
479 Ptr<Node> node = ccnx->GetObject<Node> ();
480 std::ostringstream oss;
481
482 //
483 // This has all kinds of parameters coming with, so we have to cook up our
484 // own sink.
485 //
486 oss.str ("");
487 oss << "/NodeList/" << node->GetId () << "/$ns3::CcnxL3Protocol/Drop";
488 Config::Connect (oss.str (), MakeBoundCallback (&CcnxL3ProtocolDropSinkWithContext, stream));
489 }
490
491 g_faceStreamMapCcnx[std::make_pair (ccnx, face)] = stream;
492}
493
494} // namespace ns3