More progress on CCNx stack. Now we have helpers. Everything compiles, but not yet working
diff --git a/helper/ccnx-stack-helper.cc b/helper/ccnx-stack-helper.cc
new file mode 100644
index 0000000..836c379
--- /dev/null
+++ b/helper/ccnx-stack-helper.cc
@@ -0,0 +1,494 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2011 UCLA
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author:
+ */
+
+/**
+ * \ingroup ccnx
+ * \defgroup CcnxStackModel Ccnx Stack Model
+ *
+ * \section CcnxStackTracingModel Tracing in the Ccnx Stack
+ *
+ * The ccnx stack provides a number of trace sources in its various
+ * protocol implementations. These trace sources can be hooked using your own
+ * custom trace code, or you can use our helper functions in some cases to
+ * arrange for tracing to be enabled.
+ *
+ * \subsection CcnxStackCcnxTracingModel Tracing in Ccnx
+ *
+ * The Ccnx layer three protocol provides three trace hooks. These are the
+ * "Tx" (ns3::CcnxL3Protocol::m_txTrace), "Rx" (ns3::CcnxL3Protocol::m_rxTrace)
+ * and "Drop" (ns3::CcnxL3Protocol::m_dropTrace) trace sources.
+ *
+ * The "Tx" trace is fired in a number of situations, all of which indicate that
+ * a given packet is about to be sent down to a given ns3::CcnxFace.
+ *
+ * - \todo list Tx trace events
+ *
+ * The "Rx" trace is fired when a packet is passed from the device up to the
+ * ns3::CcnxL3Protocol::Receive function.
+ *
+ * - In the receive function, the CcnxFaceList is iterated, and if the
+ * CcnxFace corresponding to the receiving device is found to be in the
+ * UP state, the trace is fired.
+ *
+ * The "Drop" trace is fired in any case where the packet is dropped (in both
+ * the transmit and receive paths).
+ *
+ * - \todo list Drop trace events
+ */
+
+#include "ns3/assert.h"
+#include "ns3/log.h"
+#include "ns3/object.h"
+#include "ns3/names.h"
+#include "ns3/ccnx.h"
+#include "ns3/packet-socket-factory.h"
+#include "ns3/config.h"
+#include "ns3/simulator.h"
+#include "ns3/string.h"
+#include "ns3/net-device.h"
+#include "ns3/callback.h"
+#include "ns3/node.h"
+#include "ns3/core-config.h"
+#include "ns3/ccnx-forwarding-protocol.h"
+
+#include "ccnx-stack-helper.h"
+#include "ccnx-forwarding-helper.h"
+
+#include <limits>
+#include <map>
+
+NS_LOG_COMPONENT_DEFINE ("CcnxStackHelper");
+
+namespace ns3 {
+
+// Things are going to work differently here with respect to trace
+// file handling than in most places because the Tx and Rx trace
+// sources we are interested in are going to multiplex receive and
+// transmit callbacks for all Ccnx and face pairs through one
+// callback. We want packets to or from each distinct pair to go to
+// an individual file, so we have got to demultiplex the Ccnx and face
+// pair into a corresponding Ptr<PcapFileWrapper> at the callback.
+//
+// A complication in this situation is that the trace sources are
+// hooked on a protocol basis. There is no trace source hooked by an
+// Ccnx and face pair. This means that if we naively proceed to hook,
+// say, a drop trace for a given Ccnx with face 0, and then hook for
+// Ccnx with face 1 we will hook the drop trace twice and get two
+// callbacks per event. What we need to do is to hook the event once,
+// and that will result in a single callback per drop event, and the
+// trace source will provide the face which we filter on in the trace
+// sink.
+//
+// This has got to continue to work properly after the helper has been
+// destroyed; but must be cleaned up at the end of time to avoid
+// leaks. Global maps of protocol/face pairs to file objects seems to
+// fit the bill.
+//
+typedef std::pair<Ptr<Ccnx>, uint32_t> FacePairCcnx;
+typedef std::map<FacePairCcnx, Ptr<PcapFileWrapper> > FaceFileMapCcnx;
+typedef std::map<FacePairCcnx, Ptr<OutputStreamWrapper> > FaceStreamMapCcnx;
+
+static FaceFileMapCcnx g_faceFileMapCcnx; /**< A mapping of Ccnx/face pairs to pcap files */
+static FaceStreamMapCcnx g_faceStreamMapCcnx; /**< A mapping of Ccnx/face pairs to ascii streams */
+
+CcnxStackHelper::CcnxStackHelper ()
+ : m_forwarding (0)
+ , m_ccnxEnabled (true)
+{
+ Initialize ();
+}
+
+// private method called by both constructor and Reset ()
+void
+CcnxStackHelper::Initialize ()
+{
+ // CcnxStaticForwardingHelper staticForwarding;
+ // CcnxGlobalForwardingHelper globalForwarding;
+ // CcnxListForwardingHelper listForwarding;
+ // listForwarding.Add (staticForwarding, 0);
+ // listForwarding.Add (globalForwarding, -10);
+ // SetForwardingHelper (listForwarding);
+}
+
+CcnxStackHelper::~CcnxStackHelper ()
+{
+ if (m_forwarding)
+ {
+ delete m_forwarding;
+ m_forwarding = 0;
+ }
+}
+
+CcnxStackHelper::CcnxStackHelper (const CcnxStackHelper &o)
+{
+ m_forwarding = o.m_forwarding->Copy ();
+ m_ccnxEnabled = o.m_ccnxEnabled;
+}
+
+CcnxStackHelper &
+CcnxStackHelper::operator = (const CcnxStackHelper &o)
+{
+ if (this == &o)
+ {
+ return *this;
+ }
+ m_forwarding = o.m_forwarding->Copy ();
+ return *this;
+}
+
+void
+CcnxStackHelper::Reset (void)
+{
+ delete m_forwarding;
+ m_forwarding = 0;
+ m_ccnxEnabled = true;
+ Initialize ();
+}
+
+void
+CcnxStackHelper::SetForwardingHelper (const CcnxForwardingHelper &forwarding)
+{
+ delete m_forwarding;
+ m_forwarding = forwarding.Copy ();
+}
+
+void
+CcnxStackHelper::SetCcnxStackInstall (bool enable)
+{
+ m_ccnxEnabled = enable;
+}
+
+void
+CcnxStackHelper::Install (NodeContainer c) const
+{
+ for (NodeContainer::Iterator i = c.Begin (); i != c.End (); ++i)
+ {
+ Install (*i);
+ }
+}
+
+void
+CcnxStackHelper::InstallAll (void) const
+{
+ Install (NodeContainer::GetGlobal ());
+}
+
+void
+CcnxStackHelper::CreateAndAggregateObjectFromTypeId (Ptr<Node> node, const std::string typeId)
+{
+ ObjectFactory factory;
+ factory.SetTypeId (typeId);
+ Ptr<Object> protocol = factory.Create <Object> ();
+ node->AggregateObject (protocol);
+}
+
+void
+CcnxStackHelper::Install (Ptr<Node> node) const
+{
+ NS_ASSERT_MSG (m_forwarding, "CcnxForwarding should be set prior calling Install() method");
+
+ if (m_ccnxEnabled)
+ {
+ if (node->GetObject<Ccnx> () != 0)
+ {
+ NS_FATAL_ERROR ("CcnxStackHelper::Install (): Installing "
+ "a CcnxStack to a node with an existing Ccnx object");
+ return;
+ }
+
+ CreateAndAggregateObjectFromTypeId (node, "ns3::CcnxL3Protocol");
+ // Set forwarding
+ Ptr<Ccnx> ccnx = node->GetObject<Ccnx> ();
+ Ptr<CcnxForwardingProtocol> ccnxForwarding = m_forwarding->Create (node);
+ ccnx->SetForwardingProtocol (ccnxForwarding);
+ }
+}
+
+void
+CcnxStackHelper::Install (std::string nodeName) const
+{
+ Ptr<Node> node = Names::Find<Node> (nodeName);
+ Install (node);
+}
+
+static void
+CcnxL3ProtocolRxTxSink (Ptr<const Packet> p, Ptr<Ccnx> ccnx, uint32_t face)
+{
+ NS_LOG_FUNCTION (p << ccnx << face);
+
+ //
+ // Since trace sources are independent of face, if we hook a source
+ // on a particular protocol we will get traces for all of its faces.
+ // We need to filter this to only report faces for which the user
+ // has expressed interest.
+ //
+ FacePairCcnx pair = std::make_pair (ccnx, face);
+ if (g_faceFileMapCcnx.find (pair) == g_faceFileMapCcnx.end ())
+ {
+ NS_LOG_INFO ("Ignoring packet to/from face " << face);
+ return;
+ }
+
+ Ptr<PcapFileWrapper> file = g_faceFileMapCcnx[pair];
+ file->Write (Simulator::Now (), p);
+}
+
+bool
+CcnxStackHelper::PcapHooked (Ptr<Ccnx> ccnx)
+{
+ for (FaceFileMapCcnx::const_iterator i = g_faceFileMapCcnx.begin ();
+ i != g_faceFileMapCcnx.end ();
+ ++i)
+ {
+ if ((*i).first.first == ccnx)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+CcnxStackHelper::EnablePcapCcnxInternal (std::string prefix, Ptr<Ccnx> ccnx, uint32_t face, bool explicitFilename)
+{
+ NS_LOG_FUNCTION (prefix << ccnx << face);
+
+ if (!m_ccnxEnabled)
+ {
+ NS_LOG_INFO ("Call to enable Ccnx pcap tracing but Ccnx not enabled");
+ return;
+ }
+
+ //
+ // We have to create a file and a mapping from protocol/face to file
+ // irrespective of how many times we want to trace a particular protocol.
+ //
+ PcapHelper pcapHelper;
+
+ std::string filename;
+ if (explicitFilename)
+ {
+ filename = prefix;
+ }
+ else
+ {
+ filename = pcapHelper.GetFilenameFromInterfacePair (prefix, ccnx, face);
+ }
+
+ Ptr<PcapFileWrapper> file = pcapHelper.CreateFile (filename, std::ios::out, PcapHelper::DLT_RAW);
+
+ //
+ // However, we only hook the trace source once to avoid multiple trace sink
+ // calls per event (connect is independent of face).
+ //
+ if (!PcapHooked (ccnx))
+ {
+ //
+ // Ptr<Ccnx> is aggregated to node and CcnxL3Protocol is aggregated to
+ // node so we can get to CcnxL3Protocol through Ccnx.
+ //
+ Ptr<CcnxL3Protocol> ccnxL3Protocol = ccnx->GetObject<CcnxL3Protocol> ();
+ NS_ASSERT_MSG (ccnxL3Protocol, "CcnxStackHelper::EnablePcapCcnxInternal(): "
+ "m_ccnxEnabled and ccnxL3Protocol inconsistent");
+
+ bool result = ccnxL3Protocol->TraceConnectWithoutContext ("Tx", MakeCallback (&CcnxL3ProtocolRxTxSink));
+ NS_ASSERT_MSG (result == true, "CcnxStackHelper::EnablePcapCcnxInternal(): "
+ "Unable to connect ccnxL3Protocol \"Tx\"");
+
+ result = ccnxL3Protocol->TraceConnectWithoutContext ("Rx", MakeCallback (&CcnxL3ProtocolRxTxSink));
+ NS_ASSERT_MSG (result == true, "CcnxStackHelper::EnablePcapCcnxInternal(): "
+ "Unable to connect ccnxL3Protocol \"Rx\"");
+ // cast result to void, to suppress ‘result’ set but not used compiler-warning
+ // for optimized builds
+ (void) result;
+ }
+
+ g_faceFileMapCcnx[std::make_pair (ccnx, face)] = file;
+}
+
+static void
+CcnxL3ProtocolDropSinkWithoutContext (
+ Ptr<OutputStreamWrapper> stream,
+ Ptr<const Packet> packet,
+ CcnxL3Protocol::DropReason reason,
+ Ptr<Ccnx> ccnx,
+ uint32_t face)
+{
+ //
+ // Since trace sources are independent of face, if we hook a source
+ // on a particular protocol we will get traces for all of its faces.
+ // We need to filter this to only report faces for which the user
+ // has expressed interest.
+ //
+ FacePairCcnx pair = std::make_pair (ccnx, face);
+ if (g_faceStreamMapCcnx.find (pair) == g_faceStreamMapCcnx.end ())
+ {
+ NS_LOG_INFO ("Ignoring packet to/from face " << face);
+ return;
+ }
+
+ *stream->GetStream () << "d " << Simulator::Now ().GetSeconds () << " " << *packet << std::endl;
+}
+
+static void
+CcnxL3ProtocolDropSinkWithContext (
+ Ptr<OutputStreamWrapper> stream,
+ std::string context,
+ Ptr<const Packet> packet,
+ CcnxL3Protocol::DropReason reason,
+ Ptr<Ccnx> ccnx,
+ uint32_t face)
+{
+ //
+ // Since trace sources are independent of face, if we hook a source
+ // on a particular protocol we will get traces for all of its faces.
+ // We need to filter this to only report faces for which the user
+ // has expressed interest.
+ //
+ FacePairCcnx pair = std::make_pair (ccnx, face);
+ if (g_faceStreamMapCcnx.find (pair) == g_faceStreamMapCcnx.end ())
+ {
+ NS_LOG_INFO ("Ignoring packet to/from face " << face);
+ return;
+ }
+
+ *stream->GetStream () << "d " << Simulator::Now ().GetSeconds () << " " << context << "(" << face << ") "
+ << *packet << std::endl;
+}
+
+bool
+CcnxStackHelper::AsciiHooked (Ptr<Ccnx> ccnx)
+{
+ for ( FaceStreamMapCcnx::const_iterator i = g_faceStreamMapCcnx.begin ();
+ i != g_faceStreamMapCcnx.end ();
+ ++i)
+ {
+ if ((*i).first.first == ccnx)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+CcnxStackHelper::EnableAsciiCcnxInternal (
+ Ptr<OutputStreamWrapper> stream,
+ std::string prefix,
+ Ptr<Ccnx> ccnx,
+ uint32_t face,
+ bool explicitFilename)
+{
+ if (!m_ccnxEnabled)
+ {
+ NS_LOG_INFO ("Call to enable Ccnx ascii tracing but Ccnx not enabled");
+ return;
+ }
+
+ //
+ // Our trace sinks are going to use packet printing, so we have to
+ // make sure that is turned on.
+ //
+ Packet::EnablePrinting ();
+
+ //
+ // If we are not provided an OutputStreamWrapper, we are expected to create
+ // one using the usual trace filename conventions and hook WithoutContext
+ // since there will be one file per context and therefore the context would
+ // be redundant.
+ //
+ if (stream == 0)
+ {
+ //
+ // Set up an output stream object to deal with private ofstream copy
+ // constructor and lifetime issues. Let the helper decide the actual
+ // name of the file given the prefix.
+ //
+ // We have to create a stream and a mapping from protocol/face to
+ // stream irrespective of how many times we want to trace a particular
+ // protocol.
+ //
+ AsciiTraceHelper asciiTraceHelper;
+
+ std::string filename;
+ if (explicitFilename)
+ {
+ filename = prefix;
+ }
+ else
+ {
+ filename = asciiTraceHelper.GetFilenameFromInterfacePair (prefix, ccnx, face);
+ }
+
+ Ptr<OutputStreamWrapper> theStream = asciiTraceHelper.CreateFileStream (filename);
+
+ //
+ // However, we only hook the trace sources once to avoid multiple trace sink
+ // calls per event (connect is independent of face).
+ //
+ if (!AsciiHooked (ccnx))
+ {
+ //
+ // The drop sink for the CcnxL3Protocol uses a different signature than
+ // the default sink, so we have to cook one up for ourselves. We can get
+ // to the Ptr<CcnxL3Protocol> through our Ptr<Ccnx> since they must both
+ // be aggregated to the same node.
+ //
+ Ptr<CcnxL3Protocol> ccnxL3Protocol = ccnx->GetObject<CcnxL3Protocol> ();
+ bool __attribute__ ((unused)) result = ccnxL3Protocol->TraceConnectWithoutContext ("Drop",
+ MakeBoundCallback (&CcnxL3ProtocolDropSinkWithoutContext, theStream));
+ NS_ASSERT_MSG (result == true, "CcnxStackHelper::EanableAsciiCcnxInternal(): "
+ "Unable to connect ccnxL3Protocol \"Drop\"");
+ }
+
+ g_faceStreamMapCcnx[std::make_pair (ccnx, face)] = theStream;
+ return;
+ }
+
+ //
+ // If we are provided an OutputStreamWrapper, we are expected to use it, and
+ // to provide a context. We are free to come up with our own context if we
+ // want, and use the AsciiTraceHelper Hook*WithContext functions, but for
+ // compatibility and simplicity, we just use Config::Connect and let it deal
+ // with the context.
+ //
+ // We need to associate the ccnx/face with a stream to express interest
+ // in tracing events on that pair, however, we only hook the trace sources
+ // once to avoid multiple trace sink calls per event (connect is independent
+ // of face).
+ //
+ if (!AsciiHooked (ccnx))
+ {
+ Ptr<Node> node = ccnx->GetObject<Node> ();
+ std::ostringstream oss;
+
+ //
+ // This has all kinds of parameters coming with, so we have to cook up our
+ // own sink.
+ //
+ oss.str ("");
+ oss << "/NodeList/" << node->GetId () << "/$ns3::CcnxL3Protocol/Drop";
+ Config::Connect (oss.str (), MakeBoundCallback (&CcnxL3ProtocolDropSinkWithContext, stream));
+ }
+
+ g_faceStreamMapCcnx[std::make_pair (ccnx, face)] = stream;
+}
+
+} // namespace ns3