600 lines
18 KiB
Plaintext
600 lines
18 KiB
Plaintext
@node Creating a new ns-3 model
|
|
@chapter Creating a new ns-3 model
|
|
|
|
@menu
|
|
* Design-approach::
|
|
* Scaffolding::
|
|
* Initial Implementation::
|
|
* Adding-a-sample-script::
|
|
* Build-core-functions-and-unit-tests::
|
|
@end menu
|
|
|
|
This chapter walks through the design process of an @command{ns-3} model.
|
|
In many research cases, users will not be satisfied to merely adapt
|
|
existing models, but may want to extend the core of the simulator in
|
|
a novel way. We will use the example of adding an ErrorModel to
|
|
a simple @command{ns-3} link as a motivating example of how one might
|
|
approach this problem and proceed through a design and implementation.
|
|
|
|
@node Design-approach
|
|
@section Design-approach
|
|
|
|
Consider how you want it to work; what should it do. Think about these
|
|
things:
|
|
@itemize @bullet
|
|
@item @emph{functionality:} What functionality should it have? What
|
|
attributes or configuration is exposed to the user?
|
|
@item @emph{reusability:} How much should others be able to reuse my design?
|
|
Can I reuse code from ns-2 to get started? How does a user integrate
|
|
the model with the rest of another simulation?
|
|
@item @emph{dependencies:} How can I reduce the introduction of
|
|
outside dependencies on my new code as much as possible (to make it
|
|
more modular)? For instance, should I avoid any dependence on IPv4 if
|
|
I want it to also be used by IPv6? Should I avoid any dependency
|
|
on IP at all?
|
|
@end itemize
|
|
|
|
Do not be hesitant to contact the ns-3-users or ns-developers list if
|
|
you have questions. In particular, it is important to think about
|
|
the public API of your new model and ask for feedback. It also helps
|
|
to let others know of your work in case you are interested in
|
|
collaborators.
|
|
|
|
@subsection Example: ErrorModel
|
|
|
|
An error model exists in ns-2. It allows packets to be passed to
|
|
a stateful object that determines, based on a random
|
|
variable, whether the packet is corrupted. The caller can then
|
|
decide what to do with the packet (drop it, etc.).
|
|
|
|
The main API of the error model is a function to pass a packet
|
|
to, and the return value of this function is a boolean that
|
|
tells the caller whether any corruption occurred. Note that
|
|
depending on the error model, the packet data buffer may or may
|
|
not be corrupted. Let's call this function "IsCorrupt()".
|
|
|
|
So far, in our design, we have:
|
|
@verbatim
|
|
class ErrorModel
|
|
{
|
|
public:
|
|
/**
|
|
* \returns true if the Packet is to be considered as errored/corrupted
|
|
* \param pkt Packet to apply error model to
|
|
*/
|
|
bool IsCorrupt (Ptr<Packet> pkt);
|
|
};
|
|
@end verbatim
|
|
Note that we do not pass a const pointer, thereby allowing the function
|
|
to modify the packet if IsCorrupt() returns true. Not all error models
|
|
will actually modify the packet; whether or not the packet data
|
|
buffer is corrupted should be documented.
|
|
|
|
We may also want specialized versions of this, such as in ns-2, so
|
|
although it is not the only design choice for polymorphism, we assume
|
|
that we will subclass a base class ErrorModel for specialized classes,
|
|
such as RateErrorModel, ListErrorModel, etc, such as is done in ns-2.
|
|
|
|
You may be thinking at this point, "Why not make IsCorrupt() a virtual
|
|
method?". That is one approach; the other is to make the public
|
|
non-virtual function indirect through a private virtual function
|
|
(this in C++ is known as the non virtual interface idiom and is
|
|
adopted in the ns-3 ErrorModel class).
|
|
|
|
Next, should this device have any dependencies on IP or other protocols?
|
|
We do not want to create dependencies on Internet protocols (the error
|
|
model should be applicable to non-Internet protocols too), so we'll keep
|
|
that in mind later.
|
|
|
|
Another consideration is how objects will include this error model.
|
|
We envision putting an explicit setter in certain NetDevice implementations,
|
|
for example.
|
|
@verbatim
|
|
/**
|
|
* Attach a receive ErrorModel to the PointToPointNetDevice.
|
|
*
|
|
* The PointToPointNetDevice may optionally include an ErrorModel in
|
|
* the packet receive chain.
|
|
*
|
|
* @see ErrorModel
|
|
* @param em Ptr to the ErrorModel.
|
|
*/
|
|
void PointToPointNetDevice::SetReceiveErrorModel(Ptr<ErrorModel> em);
|
|
@end verbatim
|
|
Again, this is not the only choice we have (error models could be aggregated
|
|
to lots of other objects), but it satisfies our primary use case, which
|
|
is to allow a user to force errors on otherwise successful packet
|
|
transmissions, at the NetDevice level.
|
|
|
|
After some thinking and looking at existing ns-2 code, here is a sample
|
|
API of a base class and first subclass that could be posted for initial
|
|
review:
|
|
@verbatim
|
|
class ErrorModel
|
|
{
|
|
public:
|
|
ErrorModel ();
|
|
virtual ~ErrorModel ();
|
|
bool IsCorrupt (Ptr<Packet> pkt);
|
|
void Reset (void);
|
|
void Enable (void);
|
|
void Disable (void);
|
|
bool IsEnabled (void) const;
|
|
private:
|
|
virtual bool DoCorrupt (Ptr<Packet> pkt) = 0;
|
|
virtual void DoReset (void) = 0;
|
|
};
|
|
|
|
enum ErrorUnit
|
|
{
|
|
EU_BIT,
|
|
EU_BYTE,
|
|
EU_PKT
|
|
};
|
|
|
|
// Determine which packets are errored corresponding to an underlying
|
|
// random variable distribution, an error rate, and unit for the rate.
|
|
class RateErrorModel : public ErrorModel
|
|
{
|
|
public:
|
|
RateErrorModel ();
|
|
virtual ~RateErrorModel ();
|
|
enum ErrorUnit GetUnit (void) const;
|
|
void SetUnit (enum ErrorUnit error_unit);
|
|
double GetRate (void) const;
|
|
void SetRate (double rate);
|
|
void SetRandomVariable (const RandomVariable &ranvar);
|
|
private:
|
|
virtual bool DoCorrupt (Ptr<Packet> pkt);
|
|
virtual void DoReset (void);
|
|
};
|
|
@end verbatim
|
|
|
|
@node Scaffolding
|
|
@section Scaffolding
|
|
|
|
Let's say that you are ready to start implementing; you have a fairly clear
|
|
picture of what you want to build, and you may have solicited some
|
|
initial review or suggestions from the list.
|
|
One way to approach the next step (implementation) is to create scaffolding
|
|
and fill in the details as the design matures.
|
|
|
|
This section walks through many of the steps you should consider to
|
|
define scaffolding, or a non-functional skeleton of what your model
|
|
will eventually implement. It is usually good practice to not wait
|
|
to get these details integrated at the end, but instead to plumb a
|
|
skeleton of your model into the system early and then add functions
|
|
later once the API and integration seems about right.
|
|
|
|
Note that you will want to modify a few things in the below presentation
|
|
for your model since if you follow the error model verbatim, the code
|
|
you produce will collide with the existing error model. The below is
|
|
just an outline of how ErrorModel was built that you can adapt to
|
|
other models.
|
|
|
|
@subsection Review the ns-3 coding style document
|
|
|
|
At this point, you may want to pause and read the ns-3 coding
|
|
style document, especially if you are considering to contribute your
|
|
code back to the project. The coding style document is linked off
|
|
the main project page:
|
|
@uref{http://www.nsnam.org/codingstyle.html,,ns-3 coding style}.
|
|
|
|
@subsection Decide where in the source tree the model will reside in
|
|
|
|
All of the ns-3 model source code is in the directory @code{src/}.
|
|
You will need to choose which subdirectory it resides in. If it is
|
|
new model code of some sort, it makes sense to put it into the
|
|
@code{src/} directory somewhere, particularly for ease of integrating
|
|
with the build system.
|
|
|
|
In the case of the error model, it is very related to the packet
|
|
class, so it makes sense to implement this in the @code{src/common/}
|
|
directory where ns-3 packets are implemented.
|
|
|
|
@subsection waf and wscript
|
|
|
|
ns-3 uses the @uref{http://www.freehackers.org/~tnagy/waf.html,,Waf}
|
|
build system. You will want to integrate your new
|
|
ns-3 uses the Waf build system. You will want to integrate your new
|
|
source files into this system. This requires that you add your files
|
|
to the @code{wscript} file found in each directory.
|
|
|
|
Let's start with empty files error-model.h and error-model.cc, and
|
|
add this to @code{src/common/wscript}. It is really just a matter
|
|
of adding the .cc file to the rest of the source files, and the .h
|
|
file to the list of the header files.
|
|
|
|
Now, pop up to the top level directory and type "./waf --check". You
|
|
shouldn't have broken anything by this operation.
|
|
@subsection include guards
|
|
Next, let's add some
|
|
@uref{http://en.wikipedia.org/wiki/Include_guard,,include guards} in our
|
|
header file.
|
|
@verbatim
|
|
#ifndef ERROR_MODEL_H
|
|
#define ERROR_MODEL_H
|
|
...
|
|
#endif
|
|
@end verbatim
|
|
|
|
@subsection namespace ns3
|
|
ns-3 uses the ns3 @uref{http://en.wikipedia.org/wiki/Namespace_(computer_science)#Use_in_common_languages,,namespace}
|
|
to isolate its symbols from other namespaces.
|
|
Typically, a user will next put an ns-3 namespace block in both the cc and
|
|
h file.
|
|
@verbatim
|
|
namespace ns3 {
|
|
...
|
|
}
|
|
@end verbatim
|
|
|
|
At this point, we have some skeletal files in which we can start defining
|
|
our new classes. The header file looks like this:
|
|
|
|
@verbatim
|
|
#ifndef ERROR_MODEL_H
|
|
#define ERROR_MODEL_H
|
|
|
|
namespace ns3 {
|
|
|
|
} // namespace ns3
|
|
#endif
|
|
@end verbatim
|
|
while the @code{error-model.cc} file simply looks like this:
|
|
@verbatim
|
|
#include "error-model.h"
|
|
|
|
namespace ns3 {
|
|
|
|
} // namespace ns3
|
|
@end verbatim
|
|
These files should compile since they don't really have any contents.
|
|
We're now ready to start adding classes.
|
|
|
|
@node Initial Implementation
|
|
@section Initial Implementation
|
|
|
|
At this point, we're still working on some scaffolding, but we can
|
|
begin to define our classes, with the functionality to be added later.
|
|
|
|
@subsection use of class Object?
|
|
|
|
This is an important design step; whether to use @code{class Object} as
|
|
a base class for your new classes.
|
|
|
|
As described in the chapter on the ns-3 @ref{Object model}, classes that
|
|
inherit from @code{class Object} get special properties:
|
|
@itemize @bullet
|
|
@item the ns-3 type and attribute system (see @ref{Attributes})
|
|
@item an object aggregation system
|
|
@item a smart-pointer reference counting system (class Ptr)
|
|
@end itemize
|
|
|
|
Classes that derive from @code{class ObjectBase} get the first two
|
|
properties above, but do not get smart pointers. Classes that
|
|
derive from @code{class RefCountBase} get only the smart-pointer
|
|
reference counting system.
|
|
|
|
In practice, @code{class Object} is the variant of the three above that
|
|
the ns-3 developer will most commonly encounter.
|
|
|
|
In our case, we want to make use of the attribute system, and we
|
|
will be passing instances of this object across the ns-3 public API,
|
|
so @code{class Object} is appropriate for us.
|
|
|
|
@subsection initial classes
|
|
|
|
One way to proceed is to start by defining the bare minimum functions
|
|
and see if they will compile. Let's review what all is needed to
|
|
implement when we derive from class Object.
|
|
|
|
@verbatim
|
|
#ifndef ERROR_MODEL_H
|
|
#define ERROR_MODEL_H
|
|
|
|
#include "ns3/object.h"
|
|
|
|
namespace ns3 {
|
|
|
|
class ErrorModel : public Object
|
|
{
|
|
public:
|
|
static TypeId GetTypeId (void);
|
|
|
|
ErrorModel ();
|
|
virtual ~ErrorModel ();
|
|
};
|
|
|
|
class RateErrorModel : public ErrorModel
|
|
{
|
|
public:
|
|
static TypeId GetTypeId (void);
|
|
|
|
RateErrorModel ();
|
|
virtual ~RateErrorModel ();
|
|
};
|
|
#endif
|
|
@end verbatim
|
|
|
|
A few things to note here. We need to include @code{object.h}. The
|
|
convention in ns-3 is that if the header file is co-located in the
|
|
same directory, it may be included without any path prefix. Therefore,
|
|
if we were implementing ErrorModel in @code{src/core} directory, we could
|
|
have just said "@code{#include "object.h"}". But we are in @code{src/common},
|
|
so we must include it as "@code{#include "ns3/object.h"}". Note also
|
|
that this goes outside the namespace declaration.
|
|
|
|
Second, each class must implement a static public member function
|
|
called @code{GetTypeId (void)}.
|
|
|
|
Third, it is a good idea to implement constructors and destructors rather
|
|
than to let the compiler generate them, and to make the destructor virtual.
|
|
In C++, note also that copy assignment operator and copy constructors
|
|
are auto-generated if they are not defined, so if you do not want those,
|
|
you should implement those as private members. This aspect of C++ is
|
|
discussed in Scott Meyers' Effective C++ book. item 45.
|
|
|
|
Let's now look at some corresponding skeletal implementation code in the .cc
|
|
file.
|
|
|
|
@verbatim
|
|
#include "error-model.h"
|
|
|
|
namespace ns3 {
|
|
|
|
NS_OBJECT_ENSURE_REGISTERED (ErrorModel);
|
|
|
|
TypeId ErrorModel::GetTypeId (void)
|
|
{
|
|
static TypeId tid = TypeId ("ns3::ErrorModel")
|
|
.SetParent<Object> ()
|
|
;
|
|
return tid;
|
|
}
|
|
|
|
ErrorModel::ErrorModel ()
|
|
{
|
|
}
|
|
|
|
ErrorModel::~ErrorModel ()
|
|
{
|
|
}
|
|
|
|
NS_OBJECT_ENSURE_REGISTERED (RateErrorModel);
|
|
|
|
TypeId RateErrorModel::GetTypeId (void)
|
|
{
|
|
static TypeId tid = TypeId ("ns3::RateErrorModel")
|
|
.SetParent<ErrorModel> ()
|
|
.AddConstructor<RateErrorModel> ()
|
|
;
|
|
return tid;
|
|
}
|
|
|
|
RateErrorModel::RateErrorModel ()
|
|
{
|
|
}
|
|
|
|
RateErrorModel::~RateErrorModel ()
|
|
{
|
|
}
|
|
@end verbatim
|
|
|
|
What is the @code{GetTypeId (void)} function? This function does a few
|
|
things. It registers a unique string into the TypeId system. It establishes
|
|
the hierarchy of objects in the attribute system (via @code{SetParent}).
|
|
It also declares that certain objects can be created via the object
|
|
creation framework (@code{AddConstructor}).
|
|
|
|
The macro @code{NS_OBJECT_ENSURE_REGISTERED (classname)} is needed also
|
|
once for every class that defines a new GetTypeId method, and it does
|
|
the actual registration of the class into the system.
|
|
The @ref{Object model} chapter discusses this in more detail.
|
|
|
|
@subsection how to include files from elsewhere
|
|
@subsection log component
|
|
|
|
@cartouche
|
|
Here, write a bit about adding ns-3 logging macros. Note that @*
|
|
LOG_COMPONENT_DEFINE is done outside the namespace ns3
|
|
@end cartouche
|
|
|
|
@subsection constructor, empty function prototypes
|
|
|
|
@subsection key variables (default values, attributes)
|
|
|
|
@subsection test program 1
|
|
|
|
|
|
|
|
@subsection Object Framework
|
|
@smallformat
|
|
@example
|
|
static const ClassId cid;
|
|
|
|
|
|
const InterfaceId ErrorModel::iid =
|
|
MakeInterfaceId ("ErrorModel", Object::iid);
|
|
|
|
const ClassId ErrorModel::cid =
|
|
MakeClassId<ErrorModel> ("ErrorModel", ErrorModel::iid);
|
|
@end example
|
|
@end smallformat
|
|
|
|
@node Adding-a-sample-script
|
|
@section Adding a sample script
|
|
|
|
At this point, one may want to try to take the basic scaffolding
|
|
defined above and add it into the system. Performing this step
|
|
now allows one to use a simpler model when plumbing into the system
|
|
and may also reveal whether any design or API modifications need to be
|
|
made. Once this is done, we will return to building out the functionality
|
|
of the ErrorModels themselves.
|
|
|
|
@subsection Add basic support in the class
|
|
|
|
@smallformat
|
|
@example
|
|
point-to-point-net-device.h
|
|
class ErrorModel;
|
|
|
|
/**
|
|
* Error model for receive packet events
|
|
*/
|
|
Ptr<ErrorModel> m_receiveErrorModel;
|
|
|
|
@end example
|
|
@end smallformat
|
|
|
|
@subsection Add Accessor
|
|
|
|
@smallformat
|
|
@example
|
|
void
|
|
PointToPointNetDevice::SetReceiveErrorModel (Ptr<ErrorModel> em)
|
|
{
|
|
NS_LOG_FUNCTION (this << em);
|
|
m_receiveErrorModel = em;
|
|
}
|
|
|
|
.AddAttribute ("ReceiveErrorModel",
|
|
"The receiver error model used to simulate packet loss",
|
|
PointerValue (),
|
|
MakePointerAccessor (&PointToPointNetDevice::m_receiveErrorModel),
|
|
MakePointerChecker<ErrorModel> ())
|
|
@end example
|
|
@end smallformat
|
|
|
|
@subsection Plumb into the system
|
|
|
|
@smallformat
|
|
@example
|
|
void PointToPointNetDevice::Receive (Ptr<Packet> packet)
|
|
{
|
|
NS_LOG_FUNCTION (this << packet);
|
|
uint16_t protocol = 0;
|
|
|
|
if (m_receiveErrorModel && m_receiveErrorModel->IsCorrupt (packet) )
|
|
{
|
|
//
|
|
// If we have an error model and it indicates that it is time to lose a
|
|
// corrupted packet, don't forward this packet up, let it go.
|
|
//
|
|
m_dropTrace (packet);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Hit the receive trace hook, strip off the point-to-point protocol header
|
|
// and forward this packet up the protocol stack.
|
|
//
|
|
m_rxTrace (packet);
|
|
ProcessHeader(packet, protocol);
|
|
m_rxCallback (this, packet, protocol, GetRemote ());
|
|
if (!m_promiscCallback.IsNull ())
|
|
{ m_promiscCallback (this, packet, protocol, GetRemote (),
|
|
GetAddress (), NetDevice::PACKET_HOST);
|
|
}
|
|
}
|
|
}
|
|
@end example
|
|
@end smallformat
|
|
|
|
@subsection Create null functional script
|
|
|
|
@smallformat
|
|
@example
|
|
simple-error-model.cc
|
|
|
|
// Error model
|
|
// We want to add an error model to node 3's NetDevice
|
|
// We can obtain a handle to the NetDevice via the channel and node
|
|
// pointers
|
|
Ptr<PointToPointNetDevice> nd3 = PointToPointTopology::GetNetDevice
|
|
(n3, channel2);
|
|
Ptr<ErrorModel> em = Create<ErrorModel> ();
|
|
nd3->SetReceiveErrorModel (em);
|
|
|
|
|
|
bool
|
|
ErrorModel::DoCorrupt (Packet& p)
|
|
{
|
|
NS_LOG_FUNCTION;
|
|
NS_LOG_UNCOND("Corrupt!");
|
|
return false;
|
|
}
|
|
@end example
|
|
@end smallformat
|
|
|
|
At this point, we can run the program with our trivial ErrorModel
|
|
plumbed into the receive path of the PointToPointNetDevice. It
|
|
prints out the string "Corrupt!" for each packet received at
|
|
node n3. Next, we return to the error model to add in a subclass
|
|
that performs more interesting error modeling.
|
|
|
|
@section Add subclass
|
|
|
|
The trivial base class ErrorModel does not do anything interesting,
|
|
but it provides a useful base class interface (Corrupt () and Reset ()),
|
|
forwarded to virtual functions that can be subclassed. Let's next
|
|
consider what we call a BasicErrorModel which is based on the
|
|
ns-2 ErrorModel class (in ns-2/queue/errmodel.@{cc,h@}).
|
|
|
|
What properties do we want this to have, from a user interface
|
|
perspective? We would like for the user to be able to trivially
|
|
swap out the type of ErrorModel used in the NetDevice. We would also
|
|
like the capability to set configurable parameters.
|
|
|
|
Here are a few simple requirements we will consider:
|
|
@itemize @bullet
|
|
@item Ability to set the random variable that governs the losses
|
|
(default is UniformVariable)
|
|
@item Ability to set the unit (bit, byte, packet, time) of granularity
|
|
over which errors are applied.
|
|
@item Ability to set the rate of errors (e.g. 10^-3) corresponding to
|
|
the above unit of granularity.
|
|
@item Ability to enable/disable (default is enabled)
|
|
@end itemize
|
|
|
|
@subsection How to subclass
|
|
|
|
We declare BasicErrorModel to be a subclass of ErrorModel as follows,
|
|
|
|
@smallformat
|
|
@example
|
|
class BasicErrorModel : public ErrorModel
|
|
{
|
|
public:
|
|
static TypeId GetTypeId (void);
|
|
...
|
|
private:
|
|
// Implement base class pure virtual functions
|
|
virtual bool DoCorrupt (Ptr<Packet> p);
|
|
virtual bool DoReset (void);
|
|
...
|
|
}
|
|
@end example
|
|
@end smallformat
|
|
|
|
and configure the subclass GetTypeId function by setting a unique
|
|
TypeId string and setting the Parent to ErrorModel:
|
|
|
|
@verbatim
|
|
TypeId RateErrorModel::GetTypeId (void)
|
|
{
|
|
static TypeId tid = TypeId ("ns3::RateErrorModel")
|
|
.SetParent<ErrorModel> ()
|
|
.AddConstructor<RateErrorModel> ()
|
|
...
|
|
@end verbatim
|
|
|
|
@node Build-core-functions-and-unit-tests
|
|
@section Build core functions and unit tests
|
|
|
|
@subsection assert macros
|
|
|
|
@subsection Writing unit tests
|
|
|
|
|