blob: 0809d58de144872f0787c3e9ce49a8fd9b50e5c3 [file] [log] [blame]
/* -*- 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: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
* Ilya Moiseenko <iliamo@cs.ucla.edu>
*/
/**
* \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/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/point-to-point-net-device.h"
#include "ns3/point-to-point-helper.h"
#include "../model/ccnx-forwarding-strategy.h"
#include "ns3/ccnx-net-device-face.h"
#include "ns3/ccnx-l3-protocol.h"
#include "ns3/ccnx-fib.h"
#include "ns3/node-list.h"
#include "ns3/loopback-net-device.h"
#include "ns3/global-router-interface.h"
#include "ns3/ipv4.h"
#include "ns3/ipv4-global-routing.h"
#include "ns3/ipv4-global-routing-ordered-nexthops.h"
#include "ns3/ipv4-routing-helper.h"
#include "ns3/ipv4-global-routing-helper.h"
#include "ns3/data-rate.h"
#include "ccnx-face-container.h"
#include "ccnx-stack-helper.h"
#include <limits>
#include <map>
#include <boost/foreach.hpp>
#define NDN_DEFAULT_DATA_SIZE 1024
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_limitsEnabled (false)
, m_needSetDefaultRoutes (false)
{
m_strategyFactory.SetTypeId ("ns3::CcnxFloodingStrategy");
}
CcnxStackHelper::~CcnxStackHelper ()
{
}
void
CcnxStackHelper::SetForwardingStrategy (std::string strategy)
{
m_strategyFactory.SetTypeId (strategy);
}
void
CcnxStackHelper::SetDefaultRoutes (bool needSet)
{
NS_LOG_FUNCTION (this << needSet);
m_needSetDefaultRoutes = needSet;
}
void
CcnxStackHelper::EnableLimits (bool enable/* = true*/, Time avgRtt/*=Seconds(0.1)*/, uint32_t avgContentObject/*=1100*/, uint32_t avgInterest/*=40*/)
{
NS_LOG_INFO ("EnableLimits: " << enable);
m_limitsEnabled = enable;
m_avgContentObjectSize = avgContentObject;
m_avgInterestSize = avgInterest;
m_avgRtt = avgRtt;
}
Ptr<CcnxFaceContainer>
CcnxStackHelper::Install (NodeContainer c) const
{
Ptr<CcnxFaceContainer> faces = Create<CcnxFaceContainer> ();
for (NodeContainer::Iterator i = c.Begin (); i != c.End (); ++i)
{
faces->AddAll (Install (*i));
}
return faces;
}
Ptr<CcnxFaceContainer>
CcnxStackHelper::InstallAll (void) const
{
return Install (NodeContainer::GetGlobal ());
}
Ptr<CcnxFaceContainer>
CcnxStackHelper::Install (Ptr<Node> node) const
{
// NS_ASSERT_MSG (m_forwarding, "SetForwardingHelper() should be set prior calling Install() method");
Ptr<CcnxFaceContainer> faces = Create<CcnxFaceContainer> ();
if (node->GetObject<Ccnx> () != 0)
{
NS_FATAL_ERROR ("CcnxStackHelper::Install (): Installing "
"a CcnxStack to a node with an existing Ccnx object");
return 0;
}
Ptr<CcnxFib> fib = CreateObject<CcnxFib> ();
node->AggregateObject (fib);
Ptr<CcnxL3Protocol> ccnx = CreateObject<CcnxL3Protocol> ();
node->AggregateObject (ccnx);
ccnx->SetForwardingStrategy
(DynamicCast<CcnxForwardingStrategy> (m_strategyFactory.Create<Object> ()));
for (uint32_t index=0; index < node->GetNDevices (); index++)
{
Ptr<NetDevice> device = node->GetDevice (index);
if (DynamicCast<LoopbackNetDevice> (device) != 0)
continue; // don't create face for a LoopbackNetDevice
Ptr<CcnxNetDeviceFace> face = Create<CcnxNetDeviceFace> (node, device);
ccnx->AddFace (face);
NS_LOG_LOGIC ("Node " << node->GetId () << ": added CcnxNetDeviceFace as face #" << *face);
if (m_needSetDefaultRoutes)
{
// default route with lowest priority possible
AddRoute (node, "/", face, std::numeric_limits<int32_t>::max ());
}
if (m_limitsEnabled)
{
NS_LOG_INFO ("Limits are enabled");
Ptr<PointToPointNetDevice> p2p = DynamicCast<PointToPointNetDevice> (device);
if (p2p == 0)
{
NS_LOG_INFO ("Non p2p interface");
continue; // only PointToPointNetDevice supports limits
}
// Setup bucket filtering
// Assume that we know average data packet size, and this size is equal default size
// Set maximum buckets (averaging over 1 second)
DataRateValue dataRate; device->GetAttribute ("DataRate", dataRate);
NS_LOG_INFO("DataRate for this link is " << dataRate.Get());
double maxInterestPackets = 1.0 * dataRate.Get ().GetBitRate () / 8.0 / m_avgContentObjectSize;
NS_LOG_INFO ("BucketMax: " << maxInterestPackets);
// Set bucket max to BDP
face->SetBucketMax (m_avgRtt.ToDouble (Time::S) * maxInterestPackets); // number of interests allowed
face->SetBucketLeak (maxInterestPackets);
}
face->SetUp ();
faces->Add (face);
}
return faces;
}
Ptr<CcnxFaceContainer>
CcnxStackHelper::Install (std::string nodeName) const
{
Ptr<Node> node = Names::Find<Node> (nodeName);
return Install (node);
}
void
CcnxStackHelper::AddRoute (Ptr<Node> node, std::string prefix, Ptr<CcnxFace> face, int32_t metric) const
{
NS_LOG_LOGIC ("[" << node->GetId () << "]$ route add " << prefix << " via " << *face << " metric " << metric);
Ptr<CcnxFib> fib = node->GetObject<CcnxFib> ();
CcnxNameComponentsValue prefixValue;
prefixValue.DeserializeFromString (prefix, MakeCcnxNameComponentsChecker ());
fib->Add (prefixValue.Get (), face, metric);
}
void
CcnxStackHelper::AddRoute (std::string nodeName, std::string prefix, uint32_t faceId, int32_t metric) const
{
Ptr<Node> node = Names::Find<Node> (nodeName);
NS_ASSERT_MSG (node != 0, "Node [" << nodeName << "] does not exist");
Ptr<Ccnx> ccnx = node->GetObject<Ccnx> ();
NS_ASSERT_MSG (ccnx != 0, "Ccnx stack should be installed on the node");
Ptr<CcnxFace> face = ccnx->GetFace (faceId);
NS_ASSERT_MSG (face != 0, "Face with ID [" << faceId << "] does not exist on node [" << nodeName << "]");
AddRoute (node, prefix, face, metric);
}
/*
void
CcnxStackHelper::AddRoute (Ptr<Node> node, std::string prefix, uint32_t faceId, int32_t metric)
{
NS_LOG_LOGIC ("[" << nodeName << "]$ route add " << prefix << " via " << faceId << " metric " << metric);
NS_ASSERT_MSG (node != 0, "Node does not exist");
Ptr<Ccnx> ccnx = node->GetObject<Ccnx> ();
Ptr<CcnxFib> fib = node->GetObject<CcnxFib> ();
Ptr<CcnxFace> face = ccnx->GetFace (faceId);
NS_ASSERT_MSG (node != 0, "Face with ID [" << faceId << "] does not exist on node [" << nodeName << "]");
CcnxNameComponentsValue prefixValue;
prefixValue.DeserializeFromString (prefix, MakeCcnxNameComponentsChecker ());
fib->Add (prefixValue.Get (), face, metric);
}
*/
// 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);
// //
// // 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)
// {
// //
// // 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;
// }
void
CcnxStackHelper::InstallFakeGlobalRoutes ()
{
for (NodeList::Iterator node = NodeList::Begin ();
node != NodeList::End ();
node ++)
{
NS_ASSERT_MSG ((*node)->GetObject<Ipv4> () != 0,
"InternetStack should be installed on all nodes");
NS_ASSERT_MSG (Ipv4RoutingHelper::GetRouting<Ipv4GlobalRoutingOrderedNexthops>
(
(*node)->GetObject<Ipv4> ()->GetRoutingProtocol ()
),
"InternetStack should have Ipv4GlobalRoutingOrderedNexthops as routing protocol");
// Example:
//
// Ipv4GlobalRoutingHelper ipv4RoutingHelper ("ns3::Ipv4GlobalRoutingUnorderedNexthops");
// stack.SetRoutingHelper (ipv4RoutingHelper);
//
Ptr<GlobalRouter> globalRouter = (*node)->GetObject<GlobalRouter> ();
if (globalRouter == 0) continue;
globalRouter->InjectRoute (Ipv4Address((*node)->GetId ()), Ipv4Mask("255.255.255.255"));
}
Ipv4GlobalRoutingHelper::PopulateAllPossibleRoutingTables ();
}
void
CcnxStackHelper::InstallRouteTo (Ptr<Node> destNode)
{
std::ostringstream destPrefix;
destPrefix << "/" << destNode->GetId ();
Ipv4Address destIpv4 = Ipv4Address(destNode->GetId ());
for (NodeList::Iterator node = NodeList::Begin ();
node != NodeList::End ();
node ++)
{
if (destNode == *node) continue;
Ptr<Ccnx> ccnx = (*node)->GetObject<Ccnx> ();
NS_ASSERT_MSG (ccnx != 0, "CCNx stack should be installed on all nodes");
Ptr<Ipv4> ipv4 = (*node)->GetObject<Ipv4> ();
NS_ASSERT_MSG (ipv4 != 0,
"InternetStack should be installed on all nodes");
Ptr<Ipv4GlobalRoutingOrderedNexthops> routing =
Ipv4RoutingHelper::GetRouting<Ipv4GlobalRoutingOrderedNexthops> (ipv4->GetRoutingProtocol ());
NS_ASSERT_MSG (routing != 0, "Ipv4GlobalRoutingOrderedNexthops should be used in InternetStack");
Ptr<Ipv4GlobalRoutingOrderedNexthops::EntryContainer>
routes = routing->Lookup (destIpv4);
NS_ASSERT_MSG (routes != 0, "Should not happen... Call the developer");
BOOST_FOREACH (const Ipv4RoutingTableEntry &entry, *routes)
{
Ptr<NetDevice> netDevice = ipv4->GetNetDevice (entry.GetInterface ());
NS_ASSERT_MSG (netDevice != 0, "Should never happen. Call the popos");
Ptr<CcnxFace> face = ccnx->GetFaceByNetDevice (netDevice);
NS_ASSERT_MSG (face != 0, "Definitely should never happen. Call the president");
AddRoute (*node, destPrefix.str(), face, entry.GetMetric ());
}
}
}
void
CcnxStackHelper::InstallRoutesToAll ()
{
for (NodeList::Iterator node = NodeList::Begin ();
node != NodeList::End ();
node ++)
{
InstallRouteTo (*node);
}
}
} // namespace ns3