4291 lines
181 KiB
Plaintext
4291 lines
181 KiB
Plaintext
\input texinfo @c -*-texinfo-*-
|
|
@c %**start of header
|
|
@setfilename ns-3.info
|
|
@settitle ns-3 tutorial
|
|
@c @setchapternewpage odd
|
|
@c %**end of header
|
|
|
|
@ifinfo
|
|
This @command{ns-3} project document is one of a set of project documents:
|
|
@itemize @bullet
|
|
@item Software Architecture
|
|
@item Manual
|
|
@item Tutorial (this document)
|
|
@end itemize
|
|
|
|
This document is written in GNU Texinfo and is to be maintained in
|
|
revision control on the @command{ns-3} code server. Both PDF and HTML versions
|
|
should be available on the server. Changes to
|
|
the document should be discussed on the ns-developers@@isi.edu mailing list.
|
|
@end ifinfo
|
|
|
|
@copying
|
|
This @command{ns-3} project document is one of a set of project documents:
|
|
@itemize @bullet
|
|
@item Software Architecture
|
|
@item Manual
|
|
@item Tutorial (this document)
|
|
@end itemize
|
|
|
|
This document is written in GNU Texinfo and is to be maintained in
|
|
revision control on the @command{ns-3} code server. Both PDF and HTML
|
|
versions should be available on the server. Changes to
|
|
the document should be discussed on the ns-developers@@isi.edu mailing list.
|
|
|
|
This software is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This software 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, see @uref{http://www.gnu.org/licenses/}.
|
|
@end copying
|
|
|
|
@titlepage
|
|
@title ns-3 Tutorial
|
|
@author ns-3 project
|
|
@author feedback: ns-developers@@isi.edu
|
|
@today{}
|
|
|
|
@c @page
|
|
@vskip 0pt plus 1filll
|
|
@insertcopying
|
|
@end titlepage
|
|
|
|
@c So the toc is printed at the start.
|
|
@anchor{Full Table of Contents}
|
|
@contents
|
|
|
|
@ifnottex
|
|
@node Top, Preface, Full Table of Contents
|
|
@top ns-3 Tutorial (html version)
|
|
|
|
For a pdf version of this manual,
|
|
see @uref{http://www.nsnam.org/docs/tutorial.pdf}.
|
|
|
|
@insertcopying
|
|
@end ifnottex
|
|
|
|
@menu
|
|
* Preface::
|
|
* Introduction::
|
|
* Resources::
|
|
* The-Basics::
|
|
* Some-Prerequisites::
|
|
* A-First-ns-3-Script::
|
|
* Tracing-at-a-Glance::
|
|
* Other-network-topologies::
|
|
* Nonlinear-Thinking::
|
|
* Summary::
|
|
* Object-Model::
|
|
* The-Doxygen-Documentation-System::
|
|
* How-To-Change-Things::
|
|
* How-To-Set-Default-Values::
|
|
* How-To-Write-A-New-Application::
|
|
@end menu
|
|
|
|
@node Preface
|
|
@chapter Preface
|
|
|
|
The purpose of this tutorial is to introduce new @command{ns-3} users to the
|
|
system in a structured way. It is sometimes difficult for new users to
|
|
glean essential information from detailed manuals and to convert this
|
|
information into working simulations. In this tutorial, we will build
|
|
several example simulations, introducing and explaining key concepts and
|
|
features as we go.
|
|
|
|
As the tutorial unfolds, we will introduce the full @command{ns-3}
|
|
documentation
|
|
and provide pointers to source code for those interested in delving deeper
|
|
into the workings of the system.
|
|
|
|
This document is one of a set of @command{ns-3} project documents:
|
|
@itemize @bullet
|
|
@item Software Architecture
|
|
@item Manual
|
|
@item Tutorial (this document)
|
|
@end itemize
|
|
|
|
This document is written in Texinfo and is to be maintained in
|
|
revision control on the @command{ns-3} code server. Both PDF and HTML versions
|
|
should be available on the server. Changes to
|
|
the document should be discussed on the ns-developers@@isi.edu mailing list.
|
|
|
|
@c ========================================================================
|
|
@c Begin document body here
|
|
@c ========================================================================
|
|
|
|
@c ========================================================================
|
|
@c Introduction
|
|
@c ========================================================================
|
|
|
|
@node Introduction
|
|
@chapter Introduction
|
|
|
|
The @command{ns-3} project is a discrete-event network simulator targeted
|
|
primarily for research and educational use. It is aimed at
|
|
comprehensively redesigning and enhancing the popular Network Simulator
|
|
@command{ns-2}.
|
|
|
|
For those familiar with @command{ns-2}, the most visible outward change
|
|
when moving to @command{ns-3} is the choice of scripting language.
|
|
@command{ns-2} is typically scripted in Tcl and results of simulations are
|
|
often visualized using the Network Animator @command{nam}. In
|
|
@command{ns-3} there is currently no visualization module, and multiple
|
|
language bindings are allowed. In this tutorial, we will concentrate on
|
|
scripting directly in C++ and interpreting results via trace files. Scripting
|
|
in other languages will typically be done via straightforward bindings of the
|
|
target language into the underlying C++.
|
|
|
|
The goal of this tutorial is to introduce new users of @command{ns-3} to enough
|
|
of the system to enable them to author simple simulation scripts and extract
|
|
useful information from the simulations. We begin by introducing some of the
|
|
other important resources that are available to those interested in using or
|
|
writing scripts, models and even those interested in making contributions to
|
|
the core @command{ns-3} system. We provide an overview of some of the
|
|
important abstractions, design patterns and idioms used when writing
|
|
@command{ns-3} scripts, and then dig right in by begining to write simulation
|
|
scripts, run them and interpret results.
|
|
|
|
After completing this tutorial, one should be able to:
|
|
@itemize @bullet
|
|
@item Find documentation resources in the distribution and on the web;
|
|
@item Download and compile the @command{ns-3} system;
|
|
@item Use the provided devices to author network simulations of fairly
|
|
significant complexity;
|
|
@item Use the default value system to configure simulations;
|
|
@item Write new @command{ns-3} applications;
|
|
@item Use the tracing subsystem.
|
|
@end itemize
|
|
|
|
@c ========================================================================
|
|
@c Resources
|
|
@c ========================================================================
|
|
|
|
@node Resources
|
|
@chapter Resources
|
|
|
|
@menu
|
|
* The-Web::
|
|
* Mercurial::
|
|
* Waf::
|
|
* Environment-Idioms-Design-Patterns::
|
|
* Socket-Programming::
|
|
@end menu
|
|
|
|
@node The-Web
|
|
@section The Web
|
|
|
|
@cindex www.nsnam.org
|
|
There are several important resources of which any @command{ns-3} user must be
|
|
aware. The main web site is located at @uref{http://www.nsnam.org}
|
|
and provides access to basic information about the @command{ns-3} system.
|
|
Detailed documentation is available through the main web site at
|
|
@uref{http://www.nsnam.org/documents.html}.
|
|
|
|
@cindex documentation
|
|
@cindex architecture
|
|
You can find documents relating to the system architecture from this page,
|
|
and also gain access to the detailed software documentation. The software
|
|
system is documented in great detail using
|
|
@uref{http://www.stack.nl/~dimitri/doxygen/,,Doxygen}. There is a Wiki that
|
|
complements the main @command{ns-3} web site which you will find at
|
|
@uref{http://www.nsnam.org/wiki/}.
|
|
|
|
You will find user and developer FAQs there as well as troubleshooting guides,
|
|
third-party contributed code, papers, etc. The source code may be found
|
|
and browsed at @uref{http://code.nsnam.org/}.
|
|
|
|
@cindex repository!ns-3-dev
|
|
@cindex repository!releases
|
|
There you will find the current development tree in the repository named
|
|
@code{ns-3-dev}. Past releases and experimental repositories of the core
|
|
developers may also be found there.
|
|
|
|
@node Mercurial
|
|
@section Mercurial
|
|
|
|
Complex software systems need some way to manage the organization and
|
|
changes to the underlying code and documentation. There are many ways to
|
|
perform this feat, and you may have heard of some of the systems that are
|
|
currently used to do this. The Concurrent Version System (CVS) is probably
|
|
the most well known.
|
|
|
|
@cindex software configuration management
|
|
@cindex Mercurial
|
|
The @command{ns-3} project uses Mercurial as its source code management system.
|
|
Although you do not need to know much about Mercurial in order to complete
|
|
this tutorial, we recommend becoming familiar with Mercurial and using it
|
|
to access the source code. Mercurial has a web site at
|
|
@uref{http://www.selenic.com/mercurial/},
|
|
from which you can get binary or source releases of this Software
|
|
Configuration Management (SCM) system. Selenic (the developer of Mercurial)
|
|
also provides a tutorial at
|
|
@uref{http://www.selenic.com/mercurial/wiki/index.cgi/Tutorial/},
|
|
and a QuickStart guide at
|
|
@uref{http://www.selenic.com/mercurial/wiki/index.cgi/QuickStart/}.
|
|
|
|
You can also find vital information about using Mercurial and @command{ns-3}
|
|
on the main @command{ns-3} web site.
|
|
|
|
@node Waf
|
|
@section Waf
|
|
|
|
@cindex Waf
|
|
@cindex make
|
|
@cindex build
|
|
Once you have source code downloaded to your local system, you will need
|
|
to compile that source to produce usable programs. Just as in the case of
|
|
source code management, there are many tools available to perform this
|
|
function. Probably the most famous of these tools is @code{make}. Along
|
|
with being the most famous, @code{make} is probably the most difficult to
|
|
use in a very large and highly configurable system. Because of this, many
|
|
alternatives have been developed. Recently these systems have been developed
|
|
using the Python language.
|
|
|
|
The build system @code{Waf} is used on the @command{ns-3} project. It is one
|
|
of the new generation of Python-based build systems. You will not need to
|
|
understand any Python to build the existing @command{ns-3} system, and will
|
|
only have to understand a tiny and intuitively obvious subset of Python in
|
|
order to extend the system in most cases.
|
|
|
|
For those interested in the gory details of Waf, the main web site can be
|
|
found at @uref{http://freehackers.org/\~tnagy/waf.html}.
|
|
|
|
@node Environment-Idioms-Design-Patterns
|
|
@section Environment, Idioms, and Design Patterns
|
|
|
|
@cindex C++
|
|
As mentioned above, scripting in @command{ns-3} is done in C++. A working
|
|
knowledge of C++ and object-oriented concepts is assumed in this document.
|
|
We will take some time to review some of the more advanced concepts or
|
|
possibly unfamiliar language features, idioms and design patterns as they
|
|
appear. We don't want this tutorial to devolve into a C++ tutorial, though,
|
|
so we do expect a basic command of the language. There are an almost
|
|
unimaginable number of sources of information on C++ available on the web or
|
|
in print.
|
|
|
|
If you are new to C++, you may want to find a tutorial- or cookbook-based
|
|
book or web site and work through at least the basic features of the language
|
|
before proceeding.
|
|
|
|
@subsection Environment
|
|
|
|
@cindex toolchain
|
|
@cindex GNU
|
|
The @command{ns-3} system uses the GNU ``toolchain'' for development.
|
|
A software toolchain is the set of programming tools available in the given
|
|
environment. For a quick review of what is included in the GNU toolchain see,
|
|
@uref{http://en.wikipedia.org/wiki/GNU_toolchain}.
|
|
|
|
@cindex Linux
|
|
Typically a @command{ns-3} author will work in Linux or a Linux-like
|
|
environment. For those running under Windows, there do exist environments
|
|
which simulate the Linux environment to various degrees. The @command{ns-3}
|
|
project supports development in the Cygwin and the MinGW environments for
|
|
these users. See @uref{http://www.cygwin.com/} and
|
|
@uref{http://www.mingw.org/} for details on downloading and using these
|
|
systems. I use Cygwin in these cases since it provides all of the Linux tools
|
|
I know and love. It can, however, sometimes be problematic due to the way it
|
|
actually does its emulation, and sometimes interactions with other Windows
|
|
software can cause problems.
|
|
|
|
@cindex Cygwin
|
|
@cindex MinGW
|
|
If you do use Cygwin or MinGW; and use Logitech products, I will save you
|
|
quite a bit of heartburn right off the bat and encourage you to take a look
|
|
at the @uref{http://www.mingw.org/MinGWiki/index.php/FAQ,,MinGW FAQ}.
|
|
|
|
@cindex Logitech
|
|
Search for ``Logitech'' and read the FAQ entry, ``why does make often
|
|
crash creating a sh.exe.stackdump file when I try to compile my source code.''
|
|
Believe it or not, the @code{Logitech Process Monitor} insinuates itself into
|
|
every DLL in the system when it is running. It can cause your Cygwin or
|
|
MinGW DLLs to die in mysterious ways and often prevents debuggers from
|
|
running. Beware of Logitech.
|
|
|
|
@subsection Idioms and Design Patterns
|
|
|
|
@cindex idiom
|
|
In any system, there are a number of problems to be solved that happen
|
|
repeatedly. Often the solutions to these problems can be generalized and
|
|
applied in a similar way across the system. These solutions are called
|
|
Design Patterns. The @command{ns-3} system relies on several classic design
|
|
patterns.
|
|
|
|
@cindex design pattern
|
|
Also, in any language, there are constructs that, while they aren't part of the
|
|
language per se, are commonly found and useful. For example, at the lowest
|
|
level a C programmer should be able to immediately recognize the purpose and
|
|
intent of the following code without having to reflect on the details:
|
|
|
|
@verbatim
|
|
for (;;)
|
|
@end verbatim
|
|
|
|
These low-level constructs, or idioms, extend upward in complexity, eventually
|
|
becoming implementations of design patterns. As you are exposed to more
|
|
and more of the @command{ns-3} system, you will begin to recognize and be
|
|
comfortable with the C++ implementations (idioms) of several important design
|
|
patterns.
|
|
|
|
@cindex functor
|
|
@cindex callback
|
|
@cindex smart pointer
|
|
The @command{ns-3} code relies heavily on
|
|
@emph{Generalized Functors, Callbacks,
|
|
Smart Pointers, Singletons, and Object Factories}. Although we will
|
|
not assume any detailed knowledge of the idioms and design patterns used
|
|
in the @command{ns-3}
|
|
system, it will be useful for readers who intend to delve deeply into the
|
|
system to understand some important related concepts. We recommend two
|
|
resources: @uref{http://www.amazon.com/Design-Patterns-Object-Oriented-Addison-Wesley-Professional/dp/0201633612/,,Design Patterns: Elements of Reusable Object-Oriented Software, Gamma et. al.} and
|
|
@uref{http://www.amazon.com/exec/obidos/ASIN/0201704315,,Modern C++ Design: Generic Programming and Design Patterns Applied, Alexandrescu}.
|
|
|
|
Gamma addresses the abstract design patterns, and Alexandrescu addresses the
|
|
C++ idioms you will often see throughout the @command{ns-3} code.
|
|
|
|
@cindex template
|
|
Almost any use of @command{ns-3} will require some basic knowledge of C++
|
|
templates.
|
|
We will discuss the high-level uses in this tutorial. However, if you venture
|
|
deeply into the source code, you will see fairly heavy use of relatively
|
|
sophisticated C++ templates in some of low-level modules of the system. The
|
|
You don't have to be a template guru to complete this tutorial but if you
|
|
expect to work in @command{ns-3} at a low level you will have to be
|
|
somewhat fluent
|
|
with templates. If you want to truly grok C++ templates we recommend,
|
|
@uref{http://www.amazon.com/Templates-Complete-Guide-David-Vandevoorde/dp/0201734842/,,C++ Templates: The Complete Guide, Vandevoorde and Josuttis}.
|
|
|
|
@node Socket-Programming
|
|
@section Socket Programming
|
|
|
|
@cindex sockets
|
|
We will assume a basic facility with the Berkeley Sockets API in the examples
|
|
used in this tutorial. If you are new to sockets, we recommend reviewing the
|
|
API and some common usage cases. For a good overview of programming TCP/IP
|
|
sockets we recommend @uref{http://www.elsevier.com/wps/product/cws_home/680765,,Practical TCP/IP Sockets in C, Donahoo and Calvert}.
|
|
|
|
There is an associated web site that includes source for the examples in the
|
|
book, which you can find at:
|
|
@uref{http://cs.baylor.edu/~donahoo/practical/CSockets/}.
|
|
|
|
If you understand the first four chapters of the book (or for those who do
|
|
not have access to a copy of the book, the echo clients and servers shown in
|
|
the website above) you will be in good shape to understand the tutorial.
|
|
There is a similar book on Multicast Sockets,
|
|
@uref{http://www.elsevier.com/wps/product/cws_home/700736,,Multicast Sockets, Makofske and Almeroth}.
|
|
that covers material you may need to understand for the multicast examples.
|
|
|
|
@c ========================================================================
|
|
@c The Basics
|
|
@c ========================================================================
|
|
|
|
@node The-Basics
|
|
@chapter The Basics
|
|
|
|
@cindex Linux
|
|
@cindex Cygwin
|
|
@cindex GNU
|
|
@cindex toolchain
|
|
From this point forward, we are going to assume that the reader is working in
|
|
Linux or a Linux emulation environment (Linux, Cygwin, etc.) and has the GNU
|
|
toolchain installed and verified.
|
|
|
|
@cindex Mercurial
|
|
@cindex Waf
|
|
We are going to assume that you have Mercurial and Waf installed and running
|
|
on the target system as described in the Getting Started section of the
|
|
@command{ns-3} web site: @uref{http://www.nsnam.org/getting_started.html}.
|
|
|
|
@section Downloading
|
|
@cindex tarball
|
|
The @command{ns-3} code is available in Mercurial repositories on the server
|
|
code.nsnam.org. You can download a tarball, but we recommend working with
|
|
Mercurial --- it will make your life easier in the long run.
|
|
|
|
@cindex repository
|
|
If you go to the following link: @uref{http://code.nsnam.org/},
|
|
you will see a number of repositories. Many are the private repositories of
|
|
the @command{ns-3} development team. The repositories of interest to you
|
|
will be
|
|
prefixed with ``ns-3''. The current development snapshot (unreleased) of
|
|
@command{ns-3} may be found at: @uref{http://code.nsnam.org/ns-3-dev/}.
|
|
|
|
The developers attempt to keep this repository in a consistent, working state
|
|
but it is a development area with unreleased code present, so you may want to
|
|
consider downloading an official release.
|
|
|
|
There will be a number of released repositories present at code.nsnam.org.
|
|
These repos will have names like ns-3.0.1 --- which referes to release 3.0.1
|
|
of the network simulator (or if you like, release 0.1 of @command{ns-3}).
|
|
Since the releases are changing at a rate of one per month, I will stick with
|
|
the more constant ns-3-dev here, but you can replace the string ns-3-dev with
|
|
your choice of release (e.g., ns-3.0.5) below. You can find the latest
|
|
version of the code either by inspection of the repository list or by going
|
|
to the ``Getting Started'' web page and looking for the latest release
|
|
identifier.
|
|
|
|
I typically create a directory called @code{repos} in my home directory under
|
|
which I keep all of my local Mercurial repositories. @emph{Hint: I will
|
|
assume you do this later in the tutorial.} If you adopt that approach, you
|
|
can get a copy of the development version of @command{ns-3} by typing
|
|
the following into your Linux shell (I use bash).
|
|
|
|
@verbatim
|
|
cd
|
|
mkdir repos
|
|
cd !$
|
|
hg clone http://code.nanam.org/ns-3-dev
|
|
@end verbatim
|
|
|
|
As the hg command executes, you should see something like the following,
|
|
|
|
@verbatim
|
|
destination directory: ns-3-dev
|
|
requesting all changes
|
|
adding changesets
|
|
adding manifests
|
|
adding file changes
|
|
added 1513 changesets with 5687 changes to 733 files
|
|
358 files updated, 0 files merged, 0 files removed, 0 files unresolved
|
|
@end verbatim
|
|
|
|
After the clone command completes, you should have a directory called
|
|
ns-3-dev under your @code{~/repos} directory, the contents of which should
|
|
look something like the following:
|
|
|
|
@verbatim
|
|
AUTHORS RELEASE_NOTES examples/ src/ waf*
|
|
LICENSE VERSION ns3/ tutorial/ waf.bat*
|
|
README doc/ samples/ utils/ wscript
|
|
@end verbatim
|
|
|
|
You are now ready to build the @command{ns-3} distribution.
|
|
|
|
@section Building
|
|
@cindex Waf!build
|
|
@cindex Waf!configure
|
|
@cindex Waf!debug
|
|
@cindex Waf!compile
|
|
We use Waf to build the @command{ns-3} project. The first thing you
|
|
will need to do is to configure the build. For reasons that will become clear
|
|
later, we are going to work with debug builds in the tutorial. To explain to
|
|
Waf that it should do debug builds you will need to execute the following
|
|
command,
|
|
|
|
@verbatim
|
|
./waf -d debug configure
|
|
@end verbatim
|
|
|
|
This runs the copy of Waf in the local directory (which is provided as a
|
|
convenience for you). As the build system checks for various dependencies
|
|
you should see output that looks similar to the following,
|
|
|
|
@verbatim
|
|
~/repos/ns-3-dev >./waf -d debug configure
|
|
Checking for program g++ : ok /usr/bin/g++
|
|
Checking for program cpp : ok /usr/bin/cpp
|
|
Checking for program ar : ok /usr/bin/ar
|
|
Checking for program ranlib : ok /usr/bin/ranlib
|
|
Checking for compiler could create programs : ok
|
|
Checking for compiler could create shared libs : ok
|
|
Checking for compiler could create static libs : ok
|
|
Checking for flags -Wall : ok
|
|
Checking for flags -O2 : ok
|
|
Checking for flags -g -DDEBUG : ok
|
|
Checking for flags -g3 -O0 -DDEBUG : ok
|
|
Checking for g++ : ok
|
|
Checking for header stdlib.h : ok
|
|
Checking for header stdlib.h : ok
|
|
Checking for header signal.h : ok
|
|
Checking for high precision time implementation: 128-bit integer
|
|
Checking for header stdint.h : ok
|
|
Checking for header inttypes.h : ok
|
|
Checking for header sys/inttypes.h : not found
|
|
Configuration finished successfully; project is now ready to build.
|
|
~/repos/ns-3-dev >
|
|
@end verbatim
|
|
|
|
The build system is now configured and you can build the debug versions of
|
|
the @command{ns-3} programs by simply typing,
|
|
|
|
@verbatim
|
|
./waf
|
|
@end verbatim
|
|
|
|
You will see many Waf status messages displayed as the system compiles. The
|
|
most important is the last one,
|
|
|
|
@verbatim
|
|
Compilation finished successfully
|
|
@end verbatim
|
|
|
|
@section Running a Script
|
|
@cindex Waf!run
|
|
We typically run scripts under the control of Waf. This allows the build
|
|
system to ensure that the shared library paths are set correctly and that
|
|
the libraries are available at run time. To run a program, simply use the
|
|
@code{run} option in Waf. Let's run the @command{ns-3} equivalent of the hello
|
|
world program by typing the following:
|
|
|
|
@verbatim
|
|
./waf --run hello-simulator
|
|
@end verbatim
|
|
|
|
Waf first checks to make sure that the program is built correctly and
|
|
executes a build if required. Waf then then executes the program, which
|
|
produces the following output.
|
|
|
|
@verbatim
|
|
Hello Simulator
|
|
@end verbatim
|
|
|
|
@emph{Congratulations. You are now an @command{ns-3} user.}
|
|
|
|
@c ========================================================================
|
|
@c Some Prerequisites
|
|
@c ========================================================================
|
|
|
|
@node Some-Prerequisites
|
|
@chapter Some Prerequisites
|
|
|
|
The first thing we need to do before actually starting to code is to explain
|
|
a few core concepts, abstractions and idioms in the system. Much of this may
|
|
appear transparently obvious to some, but we recommend taking the time to read
|
|
through this chapter just to ensure you are starting on a firm foundation.
|
|
|
|
@section Abstractions
|
|
|
|
In this section, we'll review some terms that are commonly used in
|
|
networking, but have a specific meaning in @command{ns-3}.
|
|
|
|
@subsection Node
|
|
@cindex Node
|
|
In Internet jargon, a computing device that connects to a network is called
|
|
a @emph{host} or sometimes an @emph{end system}. Because @command{ns-3} is a
|
|
@emph{network} simulator, not specifically an @emph{Internet} simulator, we
|
|
intentionally do not use the term host since it is closely associated with
|
|
the Internet and its protocols. Instead, we use a more generic term also
|
|
used by other simulators that originates in Graph Theory --- the @emph{node}.
|
|
|
|
@cindex Node!class
|
|
In @command{ns-3} the basic computing device abstraction is called the
|
|
node. This abstraction is represented in C++ by the class @code{Node}. The
|
|
@code{Node} class provides methods for managing the representations of
|
|
computing devices in simulations. Developers are expected to specialize the
|
|
@code{Node} in the object-oriented programming sense to create new computing
|
|
device models. In this tutorial, we will use a specialization of class
|
|
@code{Node} called @code{InternetNode}. As you might expect, the
|
|
@code{InternetNode} is a class that represents a host in the Internet sense,
|
|
and automatically provides core IPv4 networking protocols.
|
|
|
|
You should think of a @code{Node} as a computer to which you will add
|
|
functionality. One adds things like applications, protocol stacks and
|
|
peripheral cards with their associated drivers to enable the computer to do
|
|
useful work. We use the same basic model in @command{ns-3}.
|
|
|
|
@subsection Application
|
|
@cindex Application
|
|
Typically, computer software is divided into two broad classes. @emph{System
|
|
Software} organizes various computer resources such as memory, processor
|
|
cycles, disk, network, etc., according to some computing model. System
|
|
software usually does not use those resources to complete tasks that directly
|
|
benefit a user. A user would typically run an @emph{application} that acquires
|
|
and uses the resources controlled by the system software to accomplish some
|
|
goal.
|
|
|
|
@cindex system call
|
|
Often, the line of separation between system and application software is made
|
|
at the privilege level change that happens in operating system traps.
|
|
In @command{ns-3} there is no real concept of operating system and especially
|
|
no concept of privilege levels or system calls. We do, however, have the
|
|
idea of an application. Just as software applications run on computers to
|
|
perform tasks in the ``real world,'' @command{ns-3} applications run on
|
|
@command{ns-3} @code{Node}s to drive simulations in the simulated world.
|
|
|
|
@cindex Application!class
|
|
In @command{ns-3} the basic abstraction for a user program that generates some
|
|
activity to be simulated is the application. This abstraction is represented
|
|
in C++ by the class @code{Application}. The @code{Application} class provides
|
|
methods for managing the representations of our version of user-level
|
|
applications in simulations. Developers are expected to specialize the
|
|
@code{Application} in the object-oriented programming sense to create new
|
|
applications. In this tutorial, we will use specializations of class
|
|
@code{Application} called @code{UdpEchoClient} and @code{UdpEchoServer}.
|
|
As you might expect, these applications compose a client/server application set
|
|
used to generate and echo simulated network packets
|
|
|
|
@subsection Channel
|
|
@cindex Channel
|
|
|
|
In the real world, one can connect a computer to a network. Often the media
|
|
over which data flows in these netowrks are called @emph{channels}. When
|
|
you connect your Ethernet cable to the plug in the wall, you are connecting
|
|
your computer to an Ethernet communication channel. In the simulated world
|
|
of @command{ns-3} one connects a @code{Node} to an object representing a
|
|
communication channel. Here the basic communication subnetwork abstraction
|
|
is called the channel and is represented in C++ by the class @code{Channel}.
|
|
|
|
The @code{Channel} class provides methods for managing communication
|
|
subnetwork objects and connecting nodes to them. They may also be specialized
|
|
by developers in the object oriented programming sense. A @code{Channel}
|
|
specialization may model something as simple as a wire. The specialized
|
|
@code{Channel} can also model things as complicated as a large Ethernet
|
|
switch, or three-dimensional space in the case of wireless networks.
|
|
|
|
We will use specialized versions of the @code{Channel} called
|
|
@code{CsmaChannel} and @code{PointToPointChannel} in this tutorial. The
|
|
@code{CsmaChannel}, for example, models a version of a communication subnetwork
|
|
that implements a @emph{carrier sense multiple access} communication medium.
|
|
This gives us Ethernet-like functionality.
|
|
|
|
@subsection Net Device
|
|
@cindex NetDevice
|
|
@cindex Ethernet
|
|
|
|
It used to be the case that if you wanted to connect a computers to a network,
|
|
you had to buy a specific kind of network cable and a hardware device called
|
|
(in PC terminology) a @emph{peripheral card} that needed to be installed in
|
|
your computer. These cards were called Network Interface Cards, or
|
|
@emph{NIC}s. Today most computers come with the network controller hardware
|
|
built in and users don't see these building blocks.
|
|
|
|
A NIC will not work without a software driver to control the hardware. In
|
|
Unix (or Linux), a piece of peripheral hardware is classified as a
|
|
@emph{device}. Devices are controlled using @emph{device drivers}, and network
|
|
devices (NICs) are controlled using @emph{network device drivers}
|
|
collectively known as @emph{net devices}. In Unix and Linux you refer
|
|
to these net devices by names such as @emph{eth0}.
|
|
|
|
In @command{ns-3} the @emph{net device} abstraction covers both the software
|
|
driver and the simulated hardware. A net device is ``attached'' to a
|
|
@code{Node} in order to enable the @code{Node} to communicate with other
|
|
@code{Node}s in the simulation via @code{Channel}s. Just as in a real
|
|
computer, a @code{Node} may be connected to more than one @code{Channel} via
|
|
multiple @code{NetDevice}s.
|
|
|
|
The net device abstraction is represented in C++ by the class @code{NetDevice}.
|
|
The @code{NetDevice} class provides methods for managing connections to
|
|
@code{Node} and @code{Channel} objects; and may be specialized by developers
|
|
in the object-oriented programming sense. We will use the specialized version
|
|
of the @code{NetDevice} called the @code{CsmaNetDevice} in this tutorial.
|
|
Just as an Ethernet NIC is designed to work with an Ethernet network, the
|
|
@code{CsmaNetDevice} is designed to work with a @code{CsmaChannel}.
|
|
|
|
@subsection Topology Helpers
|
|
In a real network, you will find host computers with added (or built-in)
|
|
NICs. In @command{ns-3} we would say that you will find @code{Nodes} with
|
|
attached @code{NetDevices}. In a large simulated network you will need to
|
|
arrange many connections between @code{Node}s, @code{NetDevice}s and
|
|
@code{Channel}s.
|
|
|
|
Since connecting a @code{NetDevice} to a @code{Node}, and a @code{NetDevice}
|
|
to a @code{Channel} is such a common task in @command{ns-3} we provide what we
|
|
call @emph{topology helpers} to make this as easy as possible. Topology
|
|
helpers perform much of the dirty work of creating and connecting net devices.
|
|
For example, it may take several distinct method calls to create a NetDevice,
|
|
add a MAC address, connect the net device to a @code{Node} and configure
|
|
the protocol stack, and then connect the @code{NetDevice} to a @code{Channel}.
|
|
We use topology helper functions to compose those distinct operations into
|
|
an easy to use model.
|
|
|
|
Topology helper functions use the abstractions (described above) of Network
|
|
Interface Cards and Cables. When you think of adding a new kind of network,
|
|
you may think of going out to the local computer retailer and buying a kit.
|
|
This kit might include a nework cable and some number of peripheral cards and
|
|
thier associated software drivers. You can think of topology helpers in
|
|
roughly the same way. Instead of buying a kit for a given type of network,
|
|
you will use a topology helper class for a given type of network, to accomplish
|
|
the equivalent of installing the network ``kit.''
|
|
|
|
@section Important Idioms
|
|
Now that we have identified that there are C++ classes in the system called
|
|
@code{Node} and @code{InternetNode}, we need to understand how to bring
|
|
objects of these classes into existance, and manage their lifetimes. Let's
|
|
examine this in some detail here.
|
|
|
|
@cindex InternetNode
|
|
@cindex Create
|
|
@cindex Ptr
|
|
In @command{ns-3}, if we want to create an @code{InternetNode} in a
|
|
script, we will
|
|
typically do something like the following example:
|
|
|
|
@verbatim
|
|
Ptr<Node> p = Create<InternetNode> ();
|
|
@end verbatim
|
|
|
|
@cindex smart pointer
|
|
To some, it may seem intuitively obvious that we're creating an
|
|
@code{InternetNode} object and assigning responsibility for managing the
|
|
object to a smart pointer named @code{p}. For the rest of us, there may be
|
|
a lot in that line that is unfamiliar, so let's look at what this line means
|
|
in some detail.
|
|
|
|
@subsection Templates 101
|
|
@cindex template
|
|
If you are familiar with C++ templates, you may skip this section as it is
|
|
just a cursory introduction to function and class templates.
|
|
|
|
Referring back to the example line of code, reproduced below for your
|
|
convenience, the angle brackets you see in the code indicate that we are
|
|
using C++ @emph{templates}.
|
|
|
|
@verbatim
|
|
Ptr<Node> p = Create<InternetNode> ();
|
|
@end verbatim
|
|
|
|
The purpose of templates is to allow a programmer to write one version of code
|
|
that is applicable over multiple types. Some people consider templates to be
|
|
an enhancement of the C preprocessor macro functionality. At some level
|
|
this comparison reveal some similarities, but C++ templates are really
|
|
quite different.
|
|
|
|
@cindex template!declaration
|
|
@cindex template!definition
|
|
@cindex template!use
|
|
In C++, just as with most language constructs, templates are @emph{declared},
|
|
@emph{defined} and @emph{used}. A declaration of a template might look
|
|
something like,
|
|
|
|
@verbatim
|
|
template <typename T> T Add (T first, T second);
|
|
@end verbatim
|
|
|
|
@cindex template!typename
|
|
This line uses the keyword @code{template} followed by a declaration of a
|
|
type name (in this case @code{T}) in angle brackets. The angle brackets
|
|
should indicate to you that a template is being declared, defined or used.
|
|
The type name @code{T} can be thought of as a string that will be substitited
|
|
during the use phase of the template. For example, the @code{T} may be
|
|
replaced by the word @code{int}. It is this substitution that leads people
|
|
to compare templates with macros.
|
|
|
|
Without going into too much more detail, this snippet declares that a piece
|
|
of code exists that will be able to call a function @code{Add} that will
|
|
add arbitrary types together. The @code{T} will be eventually replaced by
|
|
a C++ data type name. For example,
|
|
|
|
@verbatim
|
|
T Add (T first, T second);
|
|
@end verbatim
|
|
|
|
might eventually become
|
|
|
|
@verbatim
|
|
int Add (int first, int second);
|
|
@end verbatim
|
|
|
|
If the template has been declared, we need to @emph{define} what that piece of
|
|
code will actually do. That might look something like,
|
|
|
|
@verbatim
|
|
template <typename T>
|
|
T Add (T first, T second)
|
|
{
|
|
return first + second;
|
|
}
|
|
@end verbatim
|
|
|
|
All we've done here is to provide an implementation of the template that
|
|
adds the two variables together and returns the result. Note that this
|
|
implementation works for any type that provides an @code{operator+}.
|
|
|
|
The puzzle all comes together when you understand that @emph{using} a template
|
|
causes the compiler to automatically instantiate code for a specific function
|
|
according to the given template parameters. You might use the above template
|
|
like,
|
|
|
|
@verbatim
|
|
int x, y, z;
|
|
z = Add<int> (x, y);
|
|
@end verbatim
|
|
|
|
@cindex template!instantiate
|
|
When the compiler sees @code{Add<int>} it understands that it needs to make
|
|
sure that code is instantiated (created) to perform the @code{Add} using the
|
|
specified type @code{<int>}. To a first approximation, the compiler will
|
|
replace the typename @code{T} with the specified type @code{int} and
|
|
automagically generate code equivalent to,
|
|
|
|
@verbatim
|
|
int Add (int first, int second)
|
|
{
|
|
return first + second;
|
|
}
|
|
@end verbatim
|
|
|
|
A user of the template definition could just as easily have provided a use
|
|
that assigned the type float. This would simply be done like,
|
|
|
|
@verbatim
|
|
float x, y, z;
|
|
z = Add<float> (x, y);
|
|
@end verbatim
|
|
|
|
In this case, the compiler would automatically generate code that looked like,
|
|
|
|
@verbatim
|
|
float Add (float first, float second)
|
|
{
|
|
return first + second;
|
|
}
|
|
@end verbatim
|
|
|
|
@cindex template!function
|
|
This particular kind of template programming uses what are called
|
|
@emph{function templates}. They are called function templates since you
|
|
are @emph{templating} function declarations and definitions.
|
|
|
|
@cindex template!class
|
|
Templates can also be used in conjunction with classes, in which case you are
|
|
said to be using, not too surprisingly, @emph{class templates}. The syntax and
|
|
use is similar. To declare a class template you might use something like,
|
|
|
|
@verbatim
|
|
template <typename T>
|
|
class MyStack
|
|
{
|
|
void Push (T data);
|
|
T Pop (void);
|
|
};
|
|
@end verbatim
|
|
|
|
The methods can be defined separately in a method similar to function template
|
|
definitions,
|
|
|
|
@verbatim
|
|
template <typename T> void MyStack<T>::Push (T data)
|
|
{
|
|
...
|
|
};
|
|
@end verbatim
|
|
|
|
You can then use the new templated class in the following way,
|
|
|
|
@verbatim
|
|
int x, y;
|
|
|
|
MyStack<int> stack;
|
|
stack.Push (x);
|
|
y = stack.Pop ();
|
|
@end verbatim
|
|
|
|
Similarly to the function template case, the compiler knows that it has to
|
|
automatically generate code to fill out the class and method declarations
|
|
and definitions using the appropriate type specified by @code{<int>}.
|
|
|
|
@subsection Smart Pointers 101
|
|
If you are familiar with C++ smart pointers, you may skip this section as it
|
|
is just a cursory introduction to smart pointers and intrusive reference
|
|
counting.
|
|
|
|
@cindex smart pointer
|
|
Referring back to the example line of code, partially reproduced below for
|
|
your convenience below, the left hand side is the declaration and
|
|
initialization of a class template that implements a @emph{smart pointer}.
|
|
|
|
@verbatim
|
|
Ptr<Node> p = ...
|
|
@end verbatim
|
|
|
|
To a first approximation, you can think of @code{Ptr<Node>} as the a new kind
|
|
of declaration of a pointer to a @code{Node} object. The difference is that
|
|
a smart pointer is a user-defined data type (instantiated via a templated
|
|
class) that @emph{simulates} a classical pointer but provides additional
|
|
features. As an aside, you typically pronounce @code{Ptr<Node>} as
|
|
``pooter node'' where pooter rhymes with footer.
|
|
|
|
@cindex memory management
|
|
One of the most important ``additional feature'' provided by smart pointers is
|
|
automatic memory management. Since you now understand class templates, you
|
|
will understand how the template allows us to write the pointer code once, but
|
|
allows us to point to many different kinds of objects. Later in the tutorial
|
|
you will see variations such as @code{Ptr<Ipv4>} and @code{Ptr<Channel>},
|
|
which are smart pointers to an IP version 4 object and a channel object,
|
|
respectively.
|
|
|
|
The use of built-in pointers in C and C++ is a major source of bugs. Constant
|
|
allocation of, passing of responsibility for, and deallocation of underlying
|
|
data makes it very likely that errors will occur. In one of these errors,
|
|
the usual problem is that the responsibility for deallocating a memory block
|
|
is misplaced. This may result in a memory leak or a duplicate deallocation.
|
|
Smart pointers try to prevent this kind of problem by working with the
|
|
@emph{scope} and @emph{extent} rules of the language to make memory
|
|
deallocation automatic.
|
|
|
|
The scope of a variable defines where in a program a given variable may be
|
|
referred to. The extent of a variable defines when in the program's execution
|
|
the variable has a valid value. Consider a simple subroutine that contains a
|
|
smart pointer.
|
|
|
|
@verbatim
|
|
void SimpleSubroutine (void)
|
|
{
|
|
Ptr<Node> p;
|
|
}
|
|
@end verbatim
|
|
|
|
@cindex scope
|
|
The variable named @code{p} has a scope limited to the subroutine itself. The
|
|
variable is said to @emph{come into scope} as the subroutine is entered during
|
|
execution. At this time, the constructor of the underlying class is executed
|
|
and a valid variable is available for use. When the subroutine is done
|
|
executing, the variable is said to @emph{go out of scope}. This causes the
|
|
destructor of the underlying class to be executed and the variable no longer
|
|
has a valid value. This is not a problem since it is no longer valid to refer
|
|
to the parameter. Smart pointers take advantage of these defined actions at
|
|
points where variables must be valid and become discardable to determine when
|
|
underlying data can be freed.
|
|
|
|
@cindex reference counting!intrusive
|
|
The @command{ns-3} smart pointer mechanism uses a mechanism called intrusive
|
|
reference counting to determine when a memory block should be automatically
|
|
deallocated. The term ``intrusive'' means that a reference count (a count of
|
|
variables required to have valid data) is stored in the object being managed
|
|
instead of in a proxy object. This means that each piece of memory managed by
|
|
a @command{ns-3} smart pointer includes a reference count. When a smart
|
|
pointer to a reference counted object is created, this reference count is
|
|
incremented. This indicates that a new variable requires a valid data object
|
|
be present. When a smart pointer to a reference counted object is destroyed
|
|
(for example, when going out of scope) the reference count of the managed
|
|
object is decremented. When the reference count goes to zero it means that
|
|
all smart pointers to the underlying object have gone out of scope and the
|
|
object is no longer needed by any past ``users'' of the object. This in turn
|
|
means that the object can be safely deallocated, and this is done
|
|
automatically for you as the ``last'' smart pointer goes out of scope.
|
|
|
|
Consider how this might work as you pass a smart pointer to an object down
|
|
a protocol stack. At each level of the stack, you pass the smart pointer
|
|
by value. This causes a copy of the smart pointer to be made, which
|
|
increments the reference count of the underlying object. When the
|
|
@emph{calling} method is done executing, the calling smart pointer goes out of
|
|
scope and the reference count is decremented. This leaves the single smart
|
|
pointer in the @emph{called} method with a reference to the underlying object.
|
|
When the smart pointer in the called method goes out of scope, the destructor
|
|
for the smart pointer is called. The destructor checks the reference count
|
|
of the underlying object and sees that it becomes zero. This indicates that
|
|
the object can be deallocated, and the destructor does so. This results in
|
|
the lifetime management of the underlying object being automatically managed,
|
|
a boon if you have experience with ``manual'' memory management and finding
|
|
memory leaks.
|
|
|
|
Now, we want to make this feature available as widely as possible to objects
|
|
in the @command{ns-3} system. The basic operations of the smart pointer class
|
|
are the same across any intrusively reference counted object. C++ provides a
|
|
mechanism to achieve this kind of generic behavior --- the template. Let's
|
|
examine the declaration of the smart pointer in more detail. First consider
|
|
the way you might declare and use a built-in pointer. For the sake of
|
|
simplicity, just assume that a C++ object of the class @code{MyClass} exists.
|
|
Further assume that @code{MyClass} provides one method called @code{method}.
|
|
Using built-in pointers, you could do something like the following:
|
|
|
|
@verbatim
|
|
MyClass *p = ...
|
|
p->method ();
|
|
@end verbatim
|
|
|
|
@cindex smart pointer
|
|
One of the key design points of smart pointers is that they should simulate
|
|
built-in pointers. In C++ this is done by overloading @code{operator->},
|
|
@code{operator=} and @code{operator*}. To implement a smart pointer we need
|
|
to provide a generic class that implements these operators. This generic
|
|
class should allow operations that appear as if it were a built-in pointer
|
|
to the reference counted object. Typically this is accomplished via a
|
|
relatively simple C++ class template. If you are interested in the details
|
|
of how this may be accomplished, see Alexandrescu for a good treatment,
|
|
|
|
@cindex template
|
|
Taking the template as given, in order to declare a smart pointer you will
|
|
need to create a smart pointer object and provide the template parameter
|
|
needed to instantiate the required code. This parameter will be the name
|
|
of the reference counted class to which you want to point. The smart
|
|
pointer class overrides @code{operator=} which allows initialization of the
|
|
smart pointer just as if it were a built-in pointer. The end result is that
|
|
you use smart pointers just as if they were built-in pointers:
|
|
|
|
@verbatim
|
|
SmartPointer<MyClass> p = ...
|
|
p->method ();
|
|
@end verbatim
|
|
|
|
@subsection Object Creation
|
|
@cindex Create
|
|
On the right hand side of the line of code we're examining (reproduced below
|
|
for convenience) is the creation of an @code{InternetNode} object.
|
|
|
|
@verbatim
|
|
... = Create<InternetNode> ();
|
|
@end verbatim
|
|
|
|
@cindex template!function
|
|
This turns out to be an instance of use of a C++ @emph{function template}. The
|
|
definition of the @code{Create<typename T>()} template calls the new operator
|
|
to create an object of the type T. It then creates a new smart pointer of
|
|
the appropriate type (i.e., @code{Ptr<T>}). This new smart pointer is
|
|
assigned initial responsibility for the new object which has its reference
|
|
count set to one.
|
|
|
|
Since the underlying creation mechanism is via the @code{new} operator, and
|
|
you can pass parameters to the constructor for an object, we provide several
|
|
templates that you can use for passing parameters to the object constructors.
|
|
If the constructor for the object requires a parameter, you simply pass that
|
|
parameter to the @code{Create} function like this,
|
|
|
|
@verbatim
|
|
int parm = 1;
|
|
... = Create<MyClass> (parm);
|
|
@end verbatim
|
|
|
|
We provide Create templates with up to seven parameters, so you could
|
|
conceivably use the @code{Create} template in situations such as,
|
|
|
|
@verbatim
|
|
int parm = 1;
|
|
... = Create<MyClass> (p1, p2, p3, p4, p5, p6, p7);
|
|
@end verbatim
|
|
|
|
@subsection Type Safety
|
|
Lets take one final look at the now infamous example line of code that we
|
|
have been examining for some time (again reproduced below).
|
|
|
|
@verbatim
|
|
Ptr<Node> p = Create<InternetNode> ();
|
|
@end verbatim
|
|
|
|
@cindex smart pointer
|
|
@cindex Node
|
|
@cindex Create
|
|
You may have noticed that the smart pointer on the left hand side of the
|
|
assignment is associated with the type @code{Node} and the @code{Create}
|
|
template on the right hand side creates an @code{InternetNode} object and
|
|
returns a @code{Ptr<InternetNode>} smart pointer. For this assignment of a
|
|
@code{Ptr<InternetNode>} to a @code{Ptr<Node>} to work, there must be some
|
|
kind of type conversion going on.
|
|
|
|
@cindex implicit conversion
|
|
Many programmers use @code{implicit conversions} without even realizing it
|
|
since they are sometimes so intuitive. For example, in the following code,
|
|
|
|
@verbatim
|
|
int i = 1;
|
|
double d = 2.;
|
|
if (n == d) ...
|
|
@end verbatim
|
|
|
|
@cindex standard conversion
|
|
the integer (1) is implicitly converted to a double (1.) before the comparison
|
|
takes place. This conversion is performed using what is known as a C++
|
|
@emph{standard conversion}. There are a number of standard conversions defined
|
|
by the C++ standard. Among them are,
|
|
|
|
@itemize @bullet
|
|
@item Integral Promotions
|
|
@item Integral Conversions
|
|
@item Floating Conversions
|
|
@item Pointer Conversions
|
|
@item Reference Conversions
|
|
@end itemize
|
|
|
|
@cindex assignment operator
|
|
@cindex Ptr
|
|
For the case of interest here, we need to know what happens in the
|
|
assignment operator (@code{operator=}) of our smart pointer @code{Ptr<Node>}.
|
|
This operator takes a reference to a @code{Ptr<Node>} and not a reference to
|
|
a @code{Ptr<InternetNode>}. The one situation where this works automatically
|
|
in C++ is if the ``destination'' reference is to a visible, unambiguous base
|
|
class of the ``source'' reference. In this case, the underlying pointer is
|
|
@emph{cast} from one type to the other automatically.
|
|
|
|
To summarize: The magic happens in the assignment operator. Class
|
|
@code{InternetNode} inherits from class @code{Node}. The reference to the
|
|
@code{InternetNode} object in question is, in essence, a pointer to an
|
|
@code{InternetNode} object. The @code{InternetNode} class inherits from the
|
|
@code{Node} base class in a way that makes @code{Node} visible and unambiguous.
|
|
Therefore, there exists a standard conversion from an @code{InternetNode *}
|
|
to a @code{Node *} and by extension from an @code{InternetNode &} to a
|
|
@code{Node &}. This conversion is applied automatically (and invisibly)
|
|
during paramater passing in the assignment operator we are examining.
|
|
|
|
@cindex base class
|
|
This is a rather involved way of saying there's an invisible pointer cast
|
|
to a base class happening in the assignment. That means that
|
|
|
|
@verbatim
|
|
Ptr<Node> p = Create<InternetNode> ();
|
|
@end verbatim
|
|
|
|
or,
|
|
|
|
@verbatim
|
|
Ptr<Channel> p = Create<CsmaChannel> ();
|
|
@end verbatim
|
|
|
|
will work just fine. Of course, if you try something @emph{bad} (TM), like:
|
|
|
|
@verbatim
|
|
Ptr<Node> p = Create<CsmaChannel> ();
|
|
@end verbatim
|
|
|
|
the compiler will quite appropriately complain that there is no conversion
|
|
between these completely unrelated objects (CsmaChannel and Node).
|
|
|
|
@subsection Summary
|
|
Going back to our infamous first line of @command{ns-3} code, we said that if
|
|
we want to create an InternetNode in a script, we will typically do something
|
|
like:
|
|
|
|
@verbatim
|
|
Ptr<Node> p = Create<InternetNode> ();
|
|
@end verbatim
|
|
|
|
@cindex Create
|
|
@cindex InternetNode
|
|
@cindex smart pointer
|
|
Now we know that this is really a simple statement. We create an
|
|
@code{InternetNode} object on the heap (indirecly using operator @code{new}
|
|
and passing no parameters to its constructor) and assign responsibility for
|
|
managing the new object's lifetime to a smart pointer. This smart pointer is
|
|
a pointer to a @code{Node} object, so there was a hidden cast from
|
|
@code{InternetNode} to a @code{Node} done via a standard C++ conversion.
|
|
|
|
This may have been quite a hurdle to get past that first line of code, but
|
|
we have covered quite a few of the important idioms that you'll encounter in
|
|
this tutorial.
|
|
|
|
@c ========================================================================
|
|
@c A First ns-3 script
|
|
@c ========================================================================
|
|
|
|
@node A-First-ns-3-Script
|
|
@chapter A First ns-3 script
|
|
@cindex design pattern
|
|
@cindex idiom
|
|
Lets build a simple network using the @command{ns-3} design patterns, idioms,
|
|
classes and helpers we have just looked at. If you downloaded the system as
|
|
was suggested above, you will have a release of @command{ns-3} in a directory
|
|
called @code{repos} under your home directory. Change into that directory,
|
|
where you should see a directory structure something like the following.
|
|
|
|
@verbatim
|
|
AUTHORS RELEASE_NOTES examples/ src/ waf*
|
|
LICENSE VERSION ns3/ tutorial/ waf.bat*
|
|
README doc/ samples/ utils/ wscript
|
|
@end verbatim
|
|
|
|
@cindex hello-simulator.cc
|
|
Change into the tutorial directory. You should see a file named
|
|
@code{hello-simulator.cc} located there. Copy this file into one named
|
|
@code{simple.cc}. If you open this new file in your favorite editor you will
|
|
see some copyright information and the following C++ code:
|
|
|
|
@verbatim
|
|
#include "ns3/log.h"
|
|
|
|
NS_LOG_COMPONENT_DEFINE ("HelloSimulator");
|
|
|
|
using namespace ns3;
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
LogComponentEnable ("HelloSimulator", LOG_LEVEL_INFO);
|
|
|
|
NS_LOG_INFO ("Hello Simulator");
|
|
}
|
|
@end verbatim
|
|
|
|
This is the @command{ns-3} version of the ubiquitous hello-world program. It
|
|
uses the @command{ns-3} Log module to print ``Hello Simulator'' into the
|
|
standard error output stream.
|
|
|
|
@cindex logging
|
|
Log components are named objects that provide for controlling the verbosity of
|
|
debugging output in the system. We'll have a lot more to say about logging
|
|
later on, but for now you can just consider the macro @code{NS_LOG_INFO} to be
|
|
a kind of fancy printf to the standard error.
|
|
|
|
@section A Simple Network
|
|
@cindex InternetNode
|
|
Let's create a simple network of @code{InternetNode} elements. In order to
|
|
actually create an @code{InternetNode}, you will have to include some header
|
|
files. Put the following code after the include statement in @code{simple.cc}.
|
|
|
|
@verbatim
|
|
#include "ns3/ptr.h"
|
|
#include "ns3/internet-node.h"
|
|
@end verbatim
|
|
|
|
@cindex include files
|
|
The @command{ns-3} build system places the core include files it needs into a
|
|
directory called @code{ns-3} and so whenever you need to include one of the
|
|
core files you need to explicitly code this. The file @code{ptr.h} defines
|
|
the generic smart pointer that we use. The file @code{internet-node.h}
|
|
defines the class InternetNode which, as described above, represents an IP
|
|
version 4-based computing element in the simulator.
|
|
|
|
So let's create a few new @code{InternetNode}s by adding the following lines
|
|
of code after the call to @code{NS_LOG_INFO} in the simple.cc file right
|
|
after the call to @code{NS_LOG_INFO}.
|
|
|
|
@verbatim
|
|
Ptr<Node> n0 = Create<InternetNode> ();
|
|
Ptr<Node> n1 = Create<InternetNode> ();
|
|
Ptr<Node> n2 = Create<InternetNode> ();
|
|
Ptr<Node> n3 = Create<InternetNode> ();
|
|
@end verbatim
|
|
|
|
As we now understand, this will create four @code{InternetNode} objects on
|
|
the heap and create four @code{Ptr<Node>} smart pointer objects on the stack
|
|
to manage them. You should remember that by using the smart pointers you are
|
|
freed from the responsibility to delete the objects you assign to them.
|
|
|
|
@cindex Channel
|
|
@cindex CsmaChannel
|
|
The next step is to create a channel over which these nodes can communicate.
|
|
Let's use the CsmaChannel and create a local area network that will allow us
|
|
to hook up nodes similarly to an Ethernet.
|
|
|
|
As usual, we'll need to include the file that provides the appropriate class
|
|
declarations:
|
|
|
|
@verbatim
|
|
#include "ns3/csma-channel.h"
|
|
@end verbatim
|
|
|
|
Next, Add the following line of code (typically done after node creation) to
|
|
create a channel with a five megabit per second data rate and a two
|
|
millisecond speed-of-light delay between all nodes. The idiom for creating
|
|
the channel is similar to that of the node, but the actual @code{Create}
|
|
function is hidden from us in the topology code. Observe that we are
|
|
using a Csma topology helper function to free us from the details regarding
|
|
how the Carrier Sense Multiple Access Channel is actually brought into
|
|
existence and initialized.
|
|
|
|
@verbatim
|
|
Ptr<CsmaChannel> lan =
|
|
CsmaTopology::CreateCsmaChannel (DataRate (5000000), MilliSeconds (2));
|
|
@end verbatim
|
|
|
|
@cindex idiom!unnamed parameter
|
|
You may be unfamiliar with the @emph{unnamed parameter} idiom used here.
|
|
When added to a list of parameters, the code @code{DataRate (5000000)}
|
|
constructs a DataRate object on the stack using the appropriate constructor.
|
|
The resulting object has no name, and therefore cannot be referenced
|
|
elsewhere, but is passed to the callee method where it has a valid name and
|
|
can be used. This idiom is essentially a shorthand version of the following:
|
|
|
|
@verbatim
|
|
DataRate rate (5000000);
|
|
Time latency (MilliSeconds (2));
|
|
Ptr<CsmaChannel> lan = CsmaTopology::CreateCsmaChannel (rate, latency);
|
|
@end verbatim
|
|
|
|
@cindex constructor
|
|
@cindex constructor!Time
|
|
We should pause for a moment and discuss the constructor to the @code{Time}
|
|
data type. There are a number of different constructors for these objects, and
|
|
so there are a number of ways that this initialization could have been done.
|
|
There is a constructor that takes a string argument, consisting of expressions
|
|
using the units @code{s, ms, us, ns, ps} or @code{fs}, so this could have been
|
|
written,
|
|
|
|
@verbatim
|
|
Time latency ("2ms");
|
|
@end verbatim
|
|
|
|
There are also helper functions available that create time units (one of these
|
|
was used in the example):
|
|
|
|
@itemize @bullet
|
|
@item @code{Seconds (double)}
|
|
@item @code{MilliSeconds (uint64_t)}
|
|
@item @code{MicroSeconds (uint64_t)}
|
|
@item @code{NanoSeconds (uint64_t)}
|
|
@item @code{PicoSeconds (uint64_t)}
|
|
@item @code{FemtoSeconds (uint64_t)}
|
|
@end itemize
|
|
|
|
C++ will attempt to promote parameters appropriately, but you will typically
|
|
see constructions that respect the type corrrectness of the constructor, as
|
|
in @code{Seconds (1.)} and @code{MilliSeconds (2)}. Notice that the code
|
|
@code{Seconds (1)} will work just as well as @code{Seconds (1.)} since the
|
|
integer 1 will be automatically promoted to a double 1. in the former code.
|
|
The converse will not work --- i.e., you cannot write code that says
|
|
@code{MilliSeconds (2.)} since a @emph{type demotion} would be required that
|
|
could lose information and the compiler will not do such things ``behind your
|
|
back.'' Don't be thrown off by this kind of automatic conversion.
|
|
|
|
@cindex MAC!address
|
|
Okay, now we have code to create four nodes and a local area network. The
|
|
next step is to wire the network together. We do this by adding net devices
|
|
to the node. When we add the net device, we also specify the network to which
|
|
the net device is connected and provide a MAC address appropriate to the
|
|
device and network types. Since we're creating an IP version 4 network using
|
|
a Csma channel, you may expect that we'll be using topology helpers
|
|
appropriate to those types --- the CsmaIpv4Topology helper. As you may expect,
|
|
we'll need to include some files to get the appropriate definitions:
|
|
|
|
@verbatim
|
|
#include "ns3/mac48-address.h"
|
|
#include "ns3/csma-net-device.h"
|
|
#include "ns3/csma-topology.h"
|
|
#include "ns3/csma-ipv4-topology.h"
|
|
@end verbatim
|
|
|
|
Now, all that is left is to do the ``wiring'':
|
|
|
|
@verbatim
|
|
uint32_t nd0 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n0, lan,
|
|
Mac48Address("08:00:2e:00:00:00"));
|
|
@end verbatim
|
|
|
|
[Note the additional unnamed parameter idiom usage here.]
|
|
|
|
This code calls the topology helper relating to Csma channels and IP version
|
|
four nodes. It asks to install a Csma net device ``into'' node zero
|
|
(@code{n0}) connecting the device to the channel named (@code{lan}). It also
|
|
assigns a MAC address to the net device. You can add similar lines of code
|
|
connecting the other nodes to the lan (remembering to assign new MAC
|
|
addresses).
|
|
|
|
@verbatim
|
|
uint32_t nd1 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n1, lan,
|
|
Mac48Address("08:00:2e:00:00:01"));
|
|
|
|
uint32_t nd2 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n2, lan,
|
|
Mac48Address("08:00:2e:00:00:02"));
|
|
|
|
uint32_t nd3 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n3, lan,
|
|
Mac48Address("08:00:2e:00:00:03"));
|
|
@end verbatim
|
|
|
|
@cindex IP!address
|
|
@cindex IP!network mask
|
|
@cindex multihome
|
|
Finally, we need to add IP addresses to our nodes. The pointers to the
|
|
nodes are stored in n0, n1, n2 and n3. We added net devices to each of
|
|
the nodes and remembered the net device index numbers as nd0, nd1, nd2 and
|
|
nd3. You can add multiple net devices to each node resulting in a situation
|
|
similar to a multi-homed host. Each time you add a net device, you will get
|
|
a new index. Since the IP address for a multi-homed host is associated with
|
|
a net device, we need to provide that index (which we have saved) to the
|
|
topology helper. We provide an IP version four address via the @command{ns-3}
|
|
class @code{Ipv4Address} which takes a dotted decimal string as a constructor
|
|
parameter. We also provide a network mask using the @command{ns-3} class
|
|
@code{Ipv4Mask} which also takes a dotted decimal string. The code to
|
|
perform the IP address assignment, then, looks like the following:
|
|
|
|
@verbatim
|
|
CsmaIpv4Topology::AddIpv4Address (n0, nd0, Ipv4Address ("10.1.1.1"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n1, nd1, Ipv4Address ("10.1.1.2"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n2, nd2, Ipv4Address ("10.1.1.3"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n3, nd3, Ipv4Address ("10.1.1.4"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
@end verbatim
|
|
|
|
We have now constructed a simulated network. Your code should now look
|
|
something like the following,
|
|
|
|
@verbatim
|
|
#include "ns3/log.h"
|
|
#include "ns3/ptr.h"
|
|
#include "ns3/internet-node.h"
|
|
#include "ns3/csma-channel.h"
|
|
#include "ns3/mac48-address.h"
|
|
#include "ns3/csma-net-device.h"
|
|
#include "ns3/csma-topology.h"
|
|
#include "ns3/csma-ipv4-topology.h"
|
|
|
|
NS_LOG_COMPONENT_DEFINE ("HelloSimulator");
|
|
|
|
using namespace ns3;
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
LogComponentEnable ("HelloSimulator", LOG_LEVEL_INFO);
|
|
|
|
NS_LOG_INFO ("Hello Simulator");
|
|
|
|
Ptr<Node> n0 = Create<InternetNode> ();
|
|
Ptr<Node> n1 = Create<InternetNode> ();
|
|
Ptr<Node> n2 = Create<InternetNode> ();
|
|
Ptr<Node> n3 = Create<InternetNode> ();
|
|
|
|
Ptr<CsmaChannel> lan =
|
|
CsmaTopology::CreateCsmaChannel (DataRate (5000000), MilliSeconds (2));
|
|
|
|
uint32_t nd0 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n0, lan,
|
|
Mac48Address("08:00:2e:00:00:00"));
|
|
|
|
uint32_t nd1 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n1, lan,
|
|
Mac48Address("08:00:2e:00:00:01"));
|
|
|
|
uint32_t nd2 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n2, lan,
|
|
Mac48Address("08:00:2e:00:00:02"));
|
|
|
|
uint32_t nd3 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n3, lan,
|
|
Mac48Address("08:00:2e:00:00:03"));
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n0, nd0, Ipv4Address ("10.1.1.1"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n1, nd1, Ipv4Address ("10.1.1.2"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n2, nd2, Ipv4Address ("10.1.1.3"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n3, nd3, Ipv4Address ("10.1.1.4"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
}
|
|
@end verbatim
|
|
|
|
This script won't actually do anything yet. The next trick will be to
|
|
convince our nodes to try and send some data over the network.
|
|
|
|
@section Using Applications
|
|
@cindex Create
|
|
As mentioned above, we use @code{Application}s in @command{ns-3} to generate
|
|
the data used to drive simulations. An @code{Application} is added to a
|
|
@command{ns-3} node conceptually just as if you would add an application to a
|
|
computer. When an application is created (using the @code{Create} template)
|
|
we tell the application which @code{Node} it belongs to (and therefore on
|
|
which node it is running) by passing a smart pointer to that @code{Node} in
|
|
the constructor arguments.
|
|
|
|
@subsection A UDP Echo Client Application
|
|
To use an application, we first have to load the header file in which it is
|
|
defined. For the UDP echo client, this would mean adding the line,
|
|
|
|
@verbatim
|
|
#include "ns3/udp-echo-client.h"
|
|
@end verbatim
|
|
|
|
In order to create the UDP echo client application we will need to add the
|
|
following code:
|
|
|
|
@verbatim
|
|
uint32_t packetSize = 1024;
|
|
uint16_t port = 7;
|
|
uint32_t maxPacketCount = 1;
|
|
Time interPacketInterval = Seconds (1.);
|
|
|
|
Ptr<UdpEchoClient> client = Create<UdpEchoClient> (n0, "10.1.1.2", port,
|
|
maxPacketCount, interPacketInterval, packetSize);
|
|
@end verbatim
|
|
|
|
@cindex packet
|
|
The first four lines have broken out the configuration parameters for the
|
|
application as named parameters for clarity. We are telling the application
|
|
to generate 1024 byte packets (@code{packetSize = 1024}); and to send these
|
|
packets to port 7 (@code{port = 7;}). The application is told to send at most
|
|
one packet (@code{maxPacketCount = 1;}); and to delay for one second between
|
|
packet sends (@code{interpacketInterval = Seconds(1.)}) which is not used since
|
|
only one packet is sent. We will defer addressing the type @code{Time} until
|
|
we discuss the simulator engine. For now just understand the semantics are
|
|
to wait for one second.
|
|
|
|
The code to actually create the @code{UdpEchoClient} application uses the
|
|
same creation idiom as we have used previously. Notice that we have a case
|
|
where the @code{Create} template is used to pass parameters to the constructor
|
|
of the underlying object.
|
|
|
|
@cindex implicit conversion sequence
|
|
Notice that a string is passed as the second parameter. The formal parameter
|
|
to the constructor of the @code{UdpEchoClient} object is actually an
|
|
@code{Ipv4Address}. We get away with this since C++ allows what are called
|
|
@emph{implicit conversion sequences} to occur between the argument in the
|
|
function call and the corresponding parameter in the function declaration.
|
|
Basically, C++ will try to figure out a way to convert parameters for you
|
|
transparently.
|
|
|
|
In this case the conversion sequence is based on the constructor for the
|
|
Ipv4Address that takes a @code{char const *} as a parameter. C++ notices
|
|
that @code{"10.1.1.2"} refers to a @code{char const *} and knows that it
|
|
needs to get from there to an @code{Ipv4Address}. The compiler notices that
|
|
there is an @code{Ipv4Address} constructor that takes a @code{char const *}
|
|
and so it uses that constructor transparently to arrange for the conversion.
|
|
|
|
You therefore have several options for passing this value. You can use an
|
|
explicit named variable as in the following:
|
|
|
|
@verbatim
|
|
Ipv4Address addr ("10.1.1.2");
|
|
...
|
|
|
|
Ptr<UdpEchoClient> client = Create<UdpEchoClient> (n0, addr, port,
|
|
maxPacketCount, interPacketInterval, packetSize);
|
|
@end verbatim
|
|
|
|
@cindex idiom|unnamed parameter
|
|
You can use the unnamed parameter idiom that we have previously seen:
|
|
|
|
@verbatim
|
|
Ptr<UdpEchoClient> client = Create<UdpEchoClient> (n0,
|
|
Ipv4Address ("10.1.1.2"), port, maxPacketCount, interPacketInterval,
|
|
packetSize);
|
|
@end verbatim
|
|
|
|
Or you can rely on implicit conversion sequences as we just saw:
|
|
|
|
@verbatim
|
|
Ptr<UdpEchoClient> client = Create<UdpEchoClient> (n0, "10.1.1.2", port,
|
|
maxPacketCount, interPacketInterval, packetSize);
|
|
@end verbatim
|
|
|
|
Which approach to take is a matter of style, really, and you will probably
|
|
see all three approaches taken in the @command{ns-3} code. You should be
|
|
comfortable seeing and using all three methods.
|
|
|
|
@subsection A UDP Echo Server Application
|
|
As usual, to use the UDP echo server we need to add a line to define the
|
|
application:
|
|
|
|
@verbatim
|
|
#include "ns3/udp-echo-server.h"
|
|
@end verbatim
|
|
|
|
In order to create the UDP echo server application we will need to add the
|
|
following code:
|
|
|
|
@verbatim
|
|
Ptr<UdpEchoServer> server = Create<UdpEchoServer> (n1, port);
|
|
@end verbatim
|
|
|
|
We only need to tell the application which node to reside on and which port
|
|
to listen on for UDP packets. The code to actually create the
|
|
@code{UdpEchoServer} application uses the now quite familiar @command{ns-3} object
|
|
creation idiom.
|
|
|
|
@subsection A UDP Echo Client-Server Simulation
|
|
Now we're getting somewhere. Your code should look something like the
|
|
following (let's change the log component name and program banner from
|
|
``Hello Simulator''to something more descriptive while we're at it).
|
|
|
|
@verbatim
|
|
#include "ns3/log.h"
|
|
#include "ns3/ptr.h"
|
|
#include "ns3/internet-node.h"
|
|
#include "ns3/csma-channel.h"
|
|
#include "ns3/mac48-address.h"
|
|
#include "ns3/csma-net-device.h"
|
|
#include "ns3/csma-topology.h"
|
|
#include "ns3/csma-ipv4-topology.h"
|
|
#include "ns3/udp-echo-client.h"
|
|
#include "ns3/udp-echo-server.h"
|
|
|
|
NS_LOG_COMPONENT_DEFINE ("UdpEchoSimulation");
|
|
|
|
using namespace ns3;
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
LogComponentEnable ("UdpEchoSimulation", LOG_LEVEL_INFO);
|
|
|
|
NS_LOG_INFO ("UDP Echo Simulation");
|
|
|
|
Ptr<Node> n0 = Create<InternetNode> ();
|
|
Ptr<Node> n1 = Create<InternetNode> ();
|
|
Ptr<Node> n2 = Create<InternetNode> ();
|
|
Ptr<Node> n3 = Create<InternetNode> ();
|
|
|
|
Ptr<CsmaChannel> lan =
|
|
CsmaTopology::CreateCsmaChannel (DataRate (5000000), MilliSeconds (2));
|
|
|
|
uint32_t nd0 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n0, lan,
|
|
Mac48Address("08:00:2e:00:00:00"));
|
|
|
|
uint32_t nd1 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n1, lan,
|
|
Mac48Address("08:00:2e:00:00:01"));
|
|
|
|
uint32_t nd2 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n2, lan,
|
|
Mac48Address("08:00:2e:00:00:02"));
|
|
|
|
uint32_t nd3 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n3, lan,
|
|
Mac48Address("08:00:2e:00:00:03"));
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n0, nd0, Ipv4Address ("10.1.1.1"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n1, nd1, Ipv4Address ("10.1.1.2"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n2, nd2, Ipv4Address ("10.1.1.3"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n3, nd3, Ipv4Address ("10.1.1.4"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
|
|
uint32_t packetSize = 1024;
|
|
uint16_t port = 7;
|
|
uint32_t maxPacketCount = 1;
|
|
Time interPacketInterval = Seconds (1.);
|
|
|
|
Ptr<UdpEchoClient> client = Create<UdpEchoClient> (n0, "10.1.1.2", port,
|
|
maxPacketCount, interPacketInterval, packetSize);
|
|
|
|
Ptr<UdpEchoServer> server = Create<UdpEchoServer> (n1, port);
|
|
|
|
}
|
|
@end verbatim
|
|
|
|
@section Using the Simulation Engine
|
|
@cindex model
|
|
@cindex simulation executive
|
|
You could say that the heart of the @command{ns-3} system is the
|
|
@emph{simulation engine} (sometimes called the simulation executive in other
|
|
systems).
|
|
|
|
In a computer simulation, a computer @emph{model} of a real world @emph{system}
|
|
is constructed. This is typically done to minimize cost since you do not have
|
|
to actually buy, install and maintain physical hardware. In the case of
|
|
@command{ns-3}, a model is a representation of a networking component that is
|
|
designed to imitate some number of important behaviors or characteristics of
|
|
an actual component in a real network. A system is a collection of models
|
|
arranged for the purpose of analyzing some behavior.
|
|
|
|
@section Models
|
|
@cindex CsmaNetDevice
|
|
@cindex CsmaChannel
|
|
@cindex InternetNode
|
|
@cindex NIC
|
|
@cindex CSMA
|
|
We have already encountered several @command{ns-3} models without specifically
|
|
calling them so. The @code{InternetNode}, @code{CsmaNetDevice} and
|
|
@code{CsmaChannel} objects are models of an Internet computing node, a CSMA
|
|
network interface card (NIC), and a network cable able to move data to and
|
|
from other CSMA NICs.
|
|
|
|
@cindex model
|
|
@cindex CSMA/CD
|
|
It is important to note that the @code{Csma} net devices and the @code{Csma}
|
|
channel do not correspond to any real world hardware that you can actually go
|
|
out and buy. These models implement an approximation, or subset, of the
|
|
behaviors that a real CSMA/CD network would have. In this case, the
|
|
@code{CsmaNetDevice} does not simulate collision detection (CD). It does
|
|
implement carrier sense and performs collision @emph{avoidance} using global
|
|
spatial knowledge available in the channel. This would be impossible in any
|
|
channel residing in our universe.
|
|
|
|
@cindex Ethernet
|
|
No model will fully implement @emph{all} of the behaviors of a piece of
|
|
hardware. It is important to understand what is being modeled by the
|
|
@command{ns-3} components you are using and what is not. For example, the Csma
|
|
components we use in this tutorial model a highly abstract multiple access
|
|
network that is topologically equivalent to an Ethernet. It is not necessarily
|
|
true that results found in a simulation using the Csma models will apply to
|
|
a real-world Ethernet network. You must understand what behaviors are
|
|
simulated in each of the models before trusting that any results can be
|
|
associated with real-world systems.
|
|
|
|
@section Time, Events and Callbacks
|
|
@cindex time
|
|
@cindex event
|
|
In a @emph{discrete event simulator} time is not something that @emph{flows},
|
|
nor is it something to be measured --- it is the driving force behind the
|
|
progress of the simulation. Time is progressed forward by the simulation
|
|
engine and anything that happens in the simulation is ultimately caused by
|
|
an @emph{event}. An event is some action in the system that is
|
|
@emph{scheduled} to happen at a certain time by the simulation engine. Time
|
|
does not flow continuously but steps discretely (in possibly large jumps)
|
|
from one scheduled event to another.
|
|
|
|
@cindex packet
|
|
For example, to start the flow of a packet through the system, one would have
|
|
to schedule an event with the simulation engine @emph{before} the simulation
|
|
was started. This is important since the simulation engine only jumps time
|
|
forward if there is a next event to process. The simulation stops if there
|
|
are no more events, which is equivalent to a state where there is ``nothing
|
|
more to do.'' Before the simulation starts, one schedules driving events in
|
|
terms of absolute time. For example, one could schedule an event to start
|
|
the flow of a first packet at, say, ten simulated seconds. In this case, the
|
|
simulation would start its clock at zero seconds and look for the first event
|
|
in its @emph{event queue}. It would immediately jump time forward by ten
|
|
seconds and @emph{fire} the scheduled event --- that is, make the event happen.
|
|
|
|
@cindex functor
|
|
@cindex function object
|
|
@cindex callback
|
|
@cindex Callback
|
|
In @command{ns-3} an event is basically a pre-packaged function call called a
|
|
@emph{functor}. Functors are also known as @emph{function objects}, which is
|
|
a more descriptive term --- an object (in the object-oriented programming
|
|
sense) that can be called as if it was a function. Typically one uses a
|
|
functor to implement @emph{deferred execution} of a function or method. The
|
|
most commonly encoutered form of deferred execution is in a @emph{callback}
|
|
from an I/O system. In this case, the goal would be to start an I/O
|
|
operation and return immediately, without having to wait for the operation
|
|
to complete. One asks the I/O subsytem to notify you when an operation is
|
|
complete by calling some function you provide. This provided function is
|
|
known as a callback function. [Imagine calling someone on the telephone and
|
|
asking them to do something for you. You also ask them to @emph{call you back}
|
|
when they are done.] Events in the @command{ns-3} system work conceptually
|
|
the same way, except that instead of an I/O completion driving the process,
|
|
the arrival of some simulated time drives the process. The @command{ns-3}
|
|
deferred exectution mechanism is via a class called @code{Callback}.
|
|
|
|
@cindex Time
|
|
@cindex Callback
|
|
The internal details of the classes representing @code{Time} and
|
|
@code{Callback} abstractions will be introduced as required. We won't see
|
|
events directly for some time, but you should know that they are happening
|
|
``under the sheets'' of the simulations you will be writing.
|
|
|
|
@section Driving the Simulation
|
|
@cindex Application
|
|
As mentioned previously, time is the driving force behind the progress of
|
|
a @command{ns-3} simulation. Events are scheduled to happen at certain times
|
|
by calling methods of the simulation engine, either directly or indirectly
|
|
through, for example, an @code{Application}.
|
|
|
|
In order to get the simulation engine set up and running in our code, we must
|
|
first include the language definitions required to describe time- and
|
|
simulator-specific classes:
|
|
|
|
@verbatim
|
|
#include "ns3/simulator.h"
|
|
#include "ns3/nstime.h"
|
|
@end verbatim
|
|
|
|
@cindex Application
|
|
As we have seen, we need to ``seed'' the simulation with at least one event.
|
|
In the case of an @code{Application}, a method to do this is provided. This
|
|
method must be implemented by each specialization of the class and we must
|
|
call this method in our script before the simulation starts. We can also
|
|
provide an event (indirectly) to stop the output of the application at a
|
|
certain time. This is done by adding the following lines to our script:
|
|
|
|
@verbatim
|
|
server->Start(Seconds(1.));
|
|
client->Start(Seconds(2.));
|
|
|
|
server->Stop (Seconds(10.));
|
|
client->Stop (Seconds(10.));
|
|
@end verbatim
|
|
|
|
@cindex Application
|
|
@cindex time
|
|
@cindex Time
|
|
@cindex socket
|
|
@cindex event
|
|
In the case of the UdpEchoServer, the call to @code{server->Start ()} gives
|
|
the @code{Application} the chance to schedule an event that will perform the
|
|
usual @emph{sockets} server sequence of socket creation, binding and
|
|
recvfrom (see Donahoo's UDPEchoServer.c).
|
|
|
|
In the case of the UdpEchoClient, the call to @code{client->Start ()} gives
|
|
the @code{Application} the chance to schedule an event that will perform the
|
|
usual @emph{sockets} client sequence of socket creation, sendto and recvfrom
|
|
(see Donahoo's UDPEchoClient.c).
|
|
|
|
@cindex event
|
|
Note that the start event for the server is scheduled to happen before the
|
|
start event of the client, just as you would start a server application before
|
|
you would attempt to start a client application in the real world.
|
|
|
|
@cindex socket!sendto
|
|
The @command{ns-3} equivalent of the call to @code{sendo} in the client will
|
|
schedule (immediately) the transmission of a UDP packet over the just created
|
|
socket. This will cause the packet to percolate down the protocol stack and
|
|
eventually into the channel. The channel will schedule a reception event in
|
|
the net device on the destination node. This event will eventually percolate
|
|
up into the server application. The server application will create a reply
|
|
packet and send it back down its stack and eventually back to the channel.
|
|
The channel will schedule a reception event back in the client and this will
|
|
cause the reply to be sent back up the protocol stack to the client
|
|
application.
|
|
|
|
The calls to @code{Stop ()} for both applications cause the sockets to be
|
|
torn down and therefore the sending and receiving of packets will be stopped
|
|
irrespective of other application settings (such as max packets and interval
|
|
in the client).
|
|
|
|
Finally, we need to run the simulation and when the simulation run is complete,
|
|
clean up any resources allocated during the run. This is done by the calling
|
|
the following static methods:
|
|
|
|
@verbatim
|
|
Simulator::Run ();
|
|
Simulator::Destroy ();
|
|
@end verbatim
|
|
|
|
We now have the makings of a complete @command{ns-3} network simulation. The
|
|
source code for the script should look like the following:
|
|
|
|
@verbatim
|
|
#include "ns3/log.h"
|
|
#include "ns3/ptr.h"
|
|
#include "ns3/internet-node.h"
|
|
#include "ns3/csma-channel.h"
|
|
#include "ns3/mac48-address.h"
|
|
#include "ns3/csma-net-device.h"
|
|
#include "ns3/csma-topology.h"
|
|
#include "ns3/csma-topology.h"
|
|
#include "ns3/csma-ipv4-topology.h"
|
|
#include "ns3/udp-echo-client.h"
|
|
#include "ns3/udp-echo-server.h"
|
|
#include "ns3/simulator.h"
|
|
#include "ns3/nstime.h"
|
|
|
|
NS_LOG_COMPONENT_DEFINE ("UdpEchoSimulation");
|
|
|
|
using namespace ns3;
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
LogComponentEnable ("UdpEchoSimulation", LOG_LEVEL_INFO);
|
|
|
|
NS_LOG_INFO ("UDP Echo Simulation");
|
|
|
|
Ptr<Node> n0 = Create<InternetNode> ();
|
|
Ptr<Node> n1 = Create<InternetNode> ();
|
|
Ptr<Node> n2 = Create<InternetNode> ();
|
|
Ptr<Node> n3 = Create<InternetNode> ();
|
|
|
|
Ptr<CsmaChannel> lan =
|
|
CsmaTopology::CreateCsmaChannel (DataRate (5000000), MilliSeconds (2));
|
|
|
|
uint32_t nd0 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n0, lan,
|
|
Mac48Address("08:00:2e:00:00:00"));
|
|
|
|
uint32_t nd1 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n1, lan,
|
|
Mac48Address("08:00:2e:00:00:01"));
|
|
|
|
uint32_t nd2 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n2, lan,
|
|
Mac48Address("08:00:2e:00:00:02"));
|
|
|
|
uint32_t nd3 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n3, lan,
|
|
Mac48Address("08:00:2e:00:00:03"));
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n0, nd0, Ipv4Address ("10.1.1.1"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n1, nd1, Ipv4Address ("10.1.1.2"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n2, nd2, Ipv4Address ("10.1.1.3"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n3, nd3, Ipv4Address ("10.1.1.4"),
|
|
Ipv4Mask ("255.255.255.0"));
|
|
|
|
uint32_t packetSize = 1024;
|
|
uint16_t port = 7;
|
|
uint32_t maxPacketCount = 1;
|
|
Time interPacketInterval = Seconds (1.);
|
|
|
|
Ptr<UdpEchoClient> client = Create<UdpEchoClient> (n0, "10.1.1.2", port,
|
|
maxPacketCount, interPacketInterval, packetSize);
|
|
|
|
Ptr<UdpEchoServer> server = Create<UdpEchoServer> (n1, port);
|
|
|
|
server->Start(Seconds(1.));
|
|
client->Start(Seconds(2.));
|
|
|
|
server->Stop (Seconds(10.));
|
|
client->Stop (Seconds(10.));
|
|
|
|
Simulator::Run ();
|
|
Simulator::Destroy ();
|
|
}
|
|
@end verbatim
|
|
|
|
@cindex csma-echo.cc
|
|
Just to make sure you don't get caught up in debugging typographical errors
|
|
we have provided this source code for you (along with a copyright header) in
|
|
the @code{tutorial} subdirectory of the @command{ns-3} distribution as
|
|
@code{csma-echo.cc}. We used this opportunity to do some ``clean up''
|
|
of some of our example cases by passing parameters using implicit conversion
|
|
sequences and removing some of the named parameters. [These were used for
|
|
pedagogic purposes and were not actually necessary.]
|
|
|
|
@section Building the Script
|
|
@cindex Waf
|
|
C++ is a compiled language, so you know it had to happen. We have to build
|
|
the script before we run it. As mentioned before, we use the Waf build system
|
|
which is Python-based. We have to change gears slightly and switch ourselves
|
|
to Python mode in order to proceed.
|
|
|
|
In each subdirectory of the @command{ns-3} distribution in which there are
|
|
source files, you will find two files: one will be named @code{waf} and one
|
|
will be named @code{wscript}. The former, @code{waf}, is a link that allows
|
|
one to start the build process from any subdirectory. We can ignore that one.
|
|
The file we need to deal with is @code{wscript}.
|
|
|
|
@cindex wscript
|
|
Open the file @code{ns-3-dev/tutorial/wscript} in your favorite editor
|
|
[remember I'm assuming that you have the distribution saved in a
|
|
repository under a directory called @code{repos} in you home directory.]
|
|
|
|
@cindex Python
|
|
You should see the following Python code (after an emacs mode line).
|
|
|
|
@verbatim
|
|
def build(bld):
|
|
obj = bld.create_ns3_program('hello-simulator')
|
|
obj.source = 'hello-simulator.cc'
|
|
@end verbatim
|
|
|
|
These are the only instructions required to build a simulation (I told you
|
|
it wasn't going to be too bad). The line with the method
|
|
@code{bld.create_ns3_program} tells the build system to create an object
|
|
file that is a program (executable) named @code{hello-simulator}. The
|
|
following line, with the method @code{obj.source} tells the build system that
|
|
the source file for the program is the file @code{hello-simulator.cc'} in the
|
|
local directory. The required libraries are linked for you for free.
|
|
|
|
All that needed to be done in order to build the new simulation using the new
|
|
source file was to copy the two lines describing the @code{hello-simulator}
|
|
program and change the names to @code{csma-echo}. You can see these lines
|
|
in the @code{wscript} file,
|
|
|
|
@verbatim
|
|
def build(bld):
|
|
obj = bld.create_ns3_program('hello-simulator')
|
|
obj.source = 'hello-simulator.cc'
|
|
|
|
obj = bld.create_ns3_program('csma-echo')
|
|
obj.source = 'csma-echo.cc'
|
|
|
|
...
|
|
@end verbatim
|
|
|
|
When you built the system above, you actually already built this new
|
|
simulation and a number of other examples. Since you have already configured
|
|
@code{Waf} and built the @code{csma-echo} script, you can run the simulation
|
|
in the same way as you ran the @code{hello-simulator} script using the
|
|
@code{waf --run} command:
|
|
|
|
@verbatim
|
|
~/repos/ns-3-dev/tutorial > waf --run csma-echo
|
|
Entering directory `~/repos/ns-3-dev/build'
|
|
Compilation finished successfully
|
|
UDP Echo Simulation
|
|
~/repos/ns-3-dev/tutorial >
|
|
@end verbatim
|
|
|
|
Wow! Wasn't that cool! I'm sure you can barely contain yourself at this
|
|
point. Okay, well, maybe we should figure out how to get some useful
|
|
information out of that simulation. It did run ... I promise.
|
|
|
|
@c ========================================================================
|
|
@c Tracing at a Glance
|
|
@c ========================================================================
|
|
|
|
@node Tracing-at-a-Glance
|
|
@chapter Tracing at a Glance
|
|
|
|
At this stage we have constructed a real simulation script, but have no way
|
|
of getting information out of the simulation. Returning to first principles,
|
|
a number of questions come immediately to mind: What do we mean by getting
|
|
information out of the simulation? What is information? How is this
|
|
information generated? What generates it? Where does it go? How can we
|
|
specify the quantity of information we get? How can we specify the location
|
|
of the sources? What is a source anyway?
|
|
|
|
@cindex toaster
|
|
The @command{ns-3} tracing system addresses each of these questions. At the
|
|
lowest levels, it is an extremely flexible module that comes with all of the
|
|
associated complexity that inevitably comes with flexibility. To minimize
|
|
the amount of work required to get started, we provide wrapper functions to
|
|
accomplish common tasks. This makes the higher levels of the tracing system
|
|
easy to use, but relatively inflexible. This is a common trade-off. Consider
|
|
a toaster: if you want to make toast, you can push a ``toast'' button; but if
|
|
you want to control the color of your toast, you will need a knob to adjust.
|
|
If you want to independently control slice color, you will need a number of
|
|
knobs, etc.
|
|
|
|
In this chapter, we will discuss the highest levels of the tracing system that
|
|
expose the fewest ``knobs.''
|
|
|
|
@section ASCII Trace Wrapper
|
|
@cindex ASCII
|
|
The ASCII trace wrapper is a wrapper around the @command{ns-3} low-level
|
|
tracing system that lets you get access to underlying trace events easily.
|
|
The output of a trace of a simulation run is an ASCII file --- thus the name.
|
|
In the spririt of keeping things simple, you won't be able to control or
|
|
configure the output. The details are all hidden from you, and are therefore
|
|
inaccessible at this level. Be assured that as you learn more and more about
|
|
the tracing system you will be able to control it to your heart's delight.
|
|
|
|
@subsection Tracing Queue Operations
|
|
@cindex queue
|
|
Let's just jump right in. As usual, we need to include the definitions
|
|
related to using ASCII tracing (don't edit any files quite yet):
|
|
|
|
@verbatim
|
|
#include "ns3/ascii-trace.h"
|
|
@end verbatim
|
|
|
|
We then need to add the code to the script to actually enable the ASCII tracing
|
|
code. The following code must be inserted before the call to
|
|
@code{Simulator::Run ();}:
|
|
|
|
@verbatim
|
|
AsciiTrace asciitrace ("tutorial.tr");
|
|
asciitrace.TraceAllQueues ();
|
|
@end verbatim
|
|
|
|
@cindex AsciiTrace
|
|
@cindex AsciiTrace!TraceAllQueues
|
|
The first line declares an object of type @code{AsciiTrace} named
|
|
@code{asciitrace} and passes a string parameter to its constructor. This
|
|
parameter is a file name to which all of the trace information will be written.
|
|
The last line, @code{asciitrace.TraceAllQueues ();} asks the trace object to
|
|
arrange that all queue operations (enqueue, dequeue, drop) on the queues
|
|
in all of the nodes of the system be traced.
|
|
|
|
@cindex csma-echo-ascii-trace.cc
|
|
Again, being the nice guys we are, we have provided you a file with these
|
|
changes already made. Make sure you understand what we've done before you
|
|
just move on. The new file is called @code{csma-echo-ascii-trace.cc} and is
|
|
located in the @code{tutorial} directory.
|
|
|
|
@cindex Waf
|
|
You can just type the following to run the trace version of the echo program:
|
|
|
|
@verbatim
|
|
./waf --run csma-echo-ascii-trace
|
|
@end verbatim
|
|
|
|
@cindex tutorial.tr
|
|
Just as you have seen previously, you will see some messages from @emph{Waf}
|
|
and then (I feel joy) the ``Compilation finished successfully'' message. The
|
|
next message, @code{UDP Echo Simulation} is from the running program. When
|
|
it ran, the program will have created a file named @code{tutorial.tr}.
|
|
Because of the way that Waf works, the file is not created in the local
|
|
directory, it is created at the top-level directory of the repository. So,
|
|
change into the top level directory and take a look at the file
|
|
@code{tutorial.tr} in your favorite editor.
|
|
|
|
@cindex trace event
|
|
There's a lot of information there in a pretty dense form, but the first thing
|
|
to notice is that there are a number of distinct lines in this file. It may
|
|
be difficult to see this clearly unless you widen your windows considerably.
|
|
Each line in the file corresponds to a @emph{trace event}. A trace event
|
|
happens whenever specific conditions happen in the simulation. In this case
|
|
we are tracing events on the @emph{device queue} present in every net device
|
|
on every node in the simulation. The device queue is a queue through which
|
|
every packet destined for a channel must pass --- it is the device
|
|
@emph{transmit} queue. Note that each line in the trace file begins with a
|
|
lone character (has a space after it). This character will have the following
|
|
meaning:
|
|
|
|
@cindex enqueue
|
|
@cindex dequeue
|
|
@cindex drop
|
|
@itemize @bullet
|
|
@item @code{+}: An enqueue operation occurred on the device queue;
|
|
@item @code{-}: A dequeue operation occurred on the device queue;
|
|
@item @code{d}: A packet was dropped, typically because the queue was full.
|
|
@end itemize
|
|
|
|
Let's take a more detailed view of the first line. I'll break it down into
|
|
sections (indented for clarity) with a two digit reference number on the
|
|
left side:
|
|
|
|
@verbatim
|
|
00 +
|
|
01 2
|
|
02 nodeid=0
|
|
03 device=0
|
|
04 queue-enqueue
|
|
05 pkt-uid=9
|
|
06 ETHERNET
|
|
07 length/type=0x806,
|
|
08 source=08:00:2e:00:00:00,
|
|
09 destination=ff:ff:ff:ff:ff:ff
|
|
10 ARP(request
|
|
11 source mac: 08:00:2e:00:00:00
|
|
12 source ipv4: 10.1.1.1
|
|
13 dest ipv4: 10.1.1.2)
|
|
14 ETHERNET fcs=0
|
|
@end verbatim
|
|
|
|
@cindex trace event
|
|
@cindex simulation time
|
|
The first line of this expanded trace event (reference number 00) is the
|
|
queue operation. We have a @code{+} character, so this corresponds to an
|
|
@emph{enqueue} operation. The second line (reference 01) is the simulation
|
|
time expressed in seconds. You may recall that we asked the
|
|
@code{UdpEchoClient} to start sending packets at two seconds. Here we see
|
|
confirmation that this is, indeed, happening.
|
|
|
|
@cindex node number
|
|
@cindex net device number
|
|
@cindex smart pointer
|
|
The next lines of the example listing (references 02 and 03) tell us that
|
|
this trace event originated in a given node and net device. Each time a node
|
|
is created it is given an identifying number that monotonically increases from
|
|
zero. Therefore, @code{nodeid=0} means that the node in which the given trace
|
|
event originated is the first node we created. In the case of our script,
|
|
this first node is is the node pointed to by the smart pointer @code{n0}. Not
|
|
too surpsisingly, this is also the node to which we attached the
|
|
@code{UdpEchoClient}. The device number is local to each node, and so the
|
|
device given by @code{device=0} is the first net device that we added to the
|
|
node in question. In our simulation, this corresponds to the
|
|
@code{CsmaNetDevice} we added to node zero (@code{n0}).
|
|
|
|
@cindex uid
|
|
@cindex unique ID
|
|
@cindex packet
|
|
The next line (reference 04) is a more readable form of the operation code
|
|
seen in the first line --- i.e., the character @code{+} means
|
|
@code{queue-enqueue}. Reference number 05 indicates that the @emph{unique id}
|
|
of the packet being enqueued is @code{9}. The fact that the first packet we
|
|
see has a unique ID of 9 should indicates to you that other things have
|
|
happened in the protocol stack before we got to this point. This will become
|
|
clear momentarily.
|
|
|
|
@cindex Ethernet
|
|
@cindex MAC address
|
|
Reference items 06 and 14 indicate that this is an Ethernet packet with
|
|
a zero (not computed) checksum (note the indentation to make parsing this
|
|
trace event a little easier). Reference 08 and 09 are the source and
|
|
destination addresses of this packet. The packet is from the MAC address we
|
|
assigned to the node zero net device in the script, and is destined for the
|
|
broadcast address --- this is a broadcast packet.
|
|
|
|
@cindex Address Resolution Protocol
|
|
@cindex ARP
|
|
@cindex ARP|request
|
|
Reference items 10 through 13 make clear what is happening. This is an ARP
|
|
(Address Resolution Protocol) request for the MAC address of the node on
|
|
which the @code{UdpEchoServer} resides. The protocol stack can't send a UDP
|
|
packet to be echoed until it knows (resolves) the MAC address; and this trace
|
|
event corresponds to an ARP request being queued for transmission to the local
|
|
network. The next line in the trace file (partially expanded),
|
|
|
|
@verbatim
|
|
00 -
|
|
01 2
|
|
02 nodeid=0
|
|
03 device=0
|
|
04 queue-dequeue
|
|
05 pkt-uid=9
|
|
...
|
|
@end verbatim
|
|
|
|
shows the (same) ARP request packet being dequeued from the device queue by
|
|
the net device and (implicitly) being sent down the channel to the broadcast
|
|
MAC address. We are not tracing net device reception events so we don't
|
|
actually see all of the net devices receiving the broadcast packet. We do,
|
|
however see the following in the third line of the trace file:
|
|
|
|
@verbatim
|
|
00 +
|
|
01 2.00207
|
|
02 nodeid=1
|
|
03 device=0
|
|
04 queue-enqueue
|
|
05 pkt-uid=10
|
|
06 ETHERNET
|
|
07 length/type=0x806,
|
|
08 source=08:00:2e:00:00:01,
|
|
09 destination=08:00:2e:00:00:00,
|
|
10 ARP(reply
|
|
11 source mac: 08:00:2e:00:00:01
|
|
12 source ipv4: 10.1.1.2
|
|
13 dest mac: 08:00:2e:00:00:00
|
|
14 dest ipv4: 10.1.1.1)
|
|
15 ETHERNET fcs=0
|
|
@end verbatim
|
|
|
|
@cindex simulation time
|
|
@cindex ARP|response
|
|
Notice that this is a queue-enqueue operation (references 00 and 04) happening
|
|
on node one (reference 02) at simulation time 2.00207 seconds (reference 01).
|
|
Looking at the packet payload (references 10-14) we see that this is an ARP
|
|
reply to the request sent by node one. Note that the simulation time
|
|
(reference 01) is now 2.00207 seconds. This is direct result of the data rate
|
|
(5 mb/s) and latency (2 ms) parameters that we passed to the
|
|
@code{CsmaChannel} when we created it. Clearly the ARP request packet was
|
|
sent over the channel and received approximately 2 ms later by node one. A
|
|
corresponding ARP response packet was created and enqueued on node one's net
|
|
device. It is this enqueue trace event that has being logged.
|
|
|
|
@cindex queue
|
|
@cindex queue|transmit
|
|
@cindex echo
|
|
Given the current state of affairs, the next thing you may expect to see is
|
|
this ARP request being received by node zero, but remember we are only looking
|
|
at trace events on the device @emph{transmit} queue. The reception of the ARP
|
|
response by node zero will not directly trigger any trace event in this case,
|
|
but it will enable the protocol stack to continue what it was originally doing
|
|
(trying to send an echo packet). Thus, the next line we see in the trace file
|
|
(@code{tutorial.tr}) is the first UDP echo packet being sent to the net device.
|
|
|
|
@verbatim
|
|
00 +
|
|
01 2.00415
|
|
02 nodeid=0
|
|
03 device=0
|
|
04 queue-enqueue
|
|
05 pkt-uid=7
|
|
06 ETHERNET
|
|
07 length/type=0x800,
|
|
08 source=08:00:2e:00:00:00,
|
|
09 destination=08:00:2e:00:00:01
|
|
10 IPV4(
|
|
11 tos 0x0
|
|
12 ttl 64
|
|
13 id 0
|
|
14 offset 0
|
|
15 flags [none]
|
|
16 length: 1052) 10.1.1.1 > 10.1.1.2
|
|
17 UDP(length: 1032)
|
|
18 49153 > 7
|
|
19 DATA (length 1024)
|
|
20 ETHERNET fcs=0
|
|
@end verbatim
|
|
|
|
@cindex simulation time
|
|
@cindex echo
|
|
@cindex ARP
|
|
@cindex ARP|request
|
|
@cindex ARP|response
|
|
@cindex IP
|
|
@cindex Ipv4
|
|
I won't go into too much detail about this packet, but I will point out a
|
|
few key items in the trace. First, the packet was enqueued at simulation time
|
|
of 2.00415 seconds. This time reflects the fact that the echo client
|
|
application started at 2. seconds and there were two ARP packets transmitted
|
|
across the network (two milliseconds + data transmission time each way). The
|
|
packet unique identifier (reference 05) is 7. Notice that this is a lower
|
|
number than the ARP request packet, which had a unique ID of 9. This tells
|
|
us that the UDP packet was actually created before the ARP request packet ---
|
|
which makes perfect sense since it was the attempt to send packet 7 that
|
|
triggered sending the ARP request packet 9. Note that this an Ethernet
|
|
packet (reference 06) like all other packets in this simulation, however this
|
|
particular packet carries an IPV4 payload and therefore has an IP version 4
|
|
header (indicated by references 10-16). This Ipv4 in turn contains a UDP
|
|
header (references 17, 18) and finally 1024 bytes of data (reference 20).
|
|
Clearly, this is the UDP echo packet emitted by the
|
|
@code{UdpEchoClient Application}.
|
|
|
|
The next trace event is an ARP request from node one. We can infer that node
|
|
one has received the UDP echo packet and the @code{UdpEchoServer Application}
|
|
on that node has turned the packet around. Just as node zero needed to ARP
|
|
for the MAC address of node one, now node one must ARP for the MAC address of
|
|
node zero. We see the ARP request enqueued on the transmit queue of node one;
|
|
then we see the ARP request dequeued from the tranmit queue of node one (and
|
|
implicitly transmitted to node zero). Then we see an ARP response enqueued
|
|
on the transmit queue of node zero; and finally the ARP response dequeued (and
|
|
implicitly transmitted back to node one).
|
|
|
|
This exchange is summarized in the following trace event excerpts,
|
|
|
|
@verbatim
|
|
+ 2.00786 nodeid=1 ... ARP(request ...
|
|
- 2.00786 nodeid=1 ... ARP(request ...
|
|
+ 2.00994 nodeid=0 ... ARP(reply ...
|
|
- 2.00994 nodeid=0 ... ARP(reply ...
|
|
@end verbatim
|
|
|
|
The final two trace events in the @code{tutorial.tr} file correspond to the
|
|
echoed packet being enqueued for transmission on the net device for node one,
|
|
and that packet being dequeued (and implicitly transmitted back to node zero).
|
|
|
|
@subsection Tracing Device Receive Operations
|
|
There is one final ``knob'' we can turn on the ASCII trace wrapper. We can
|
|
enable net device receive operations. In our analysis of the existing trace
|
|
file we noted several times that we inferred that a packet was received by
|
|
a given node. We will now enable the event to actually trace that operation.
|
|
|
|
All we have to do is add one more line to the file @code{csma-echo.cc} to
|
|
enable tracing of net device receive operations. The code is already in the
|
|
file, but disabled.
|
|
|
|
@cindex AsciiTrace!TraceAllNetDeviceRx
|
|
@verbatim
|
|
#if 0
|
|
asciitrace.TraceAllNetDeviceRx ();
|
|
#endif
|
|
asciitrace.TraceAllNetDeviceRx ();
|
|
@end verbatim
|
|
|
|
Change the @code{#if 0} to @code{#if 1} using your favorite editor and compile
|
|
and run the file using waf as we have done previously:
|
|
|
|
@verbatim
|
|
./waf --run csma-echo-ascii-trace
|
|
@end verbatim
|
|
|
|
@cindex ARP!request
|
|
Now if you look at the trace file (@code{tutorial.tr}) you will see some new
|
|
entries. These entries all begin with the character @code{r} indicating a
|
|
@emph{receive} trace event. Recall that the first packet sent on the network
|
|
was a broadcast ARP request. We should then see all four nodes receive a
|
|
copy of this request. This is the case, as the first four receive trace
|
|
events are,
|
|
|
|
@verbatim
|
|
r 2.00207 nodeid=0 device=0 dev-rx pkt-uid=9 ARP(request ...
|
|
r 2.00207 nodeid=1 device=0 dev-rx pkt-uid=9 ARP(request ...
|
|
r 2.00207 nodeid=2 device=0 dev-rx pkt-uid=9 ARP(request ...
|
|
r 2.00207 nodeid=3 device=0 dev-rx pkt-uid=9 ARP(request ...
|
|
@end verbatim
|
|
|
|
@cindex unique ID
|
|
You can see that a copy of the broadcast packet with unique ID 9 was received
|
|
by the net devices on nodes 0, 1, 2 and 3. We leave it up to you to parse the
|
|
rest of the trace file and understand the remaining reception events.
|
|
|
|
@section PCAP Trace Wrapper
|
|
@cindex pcap
|
|
@cindex Wireshark
|
|
The @command{ns-3} @emph{pcap trace wrapper} is used to create trace files in
|
|
@code{.pcap} format. The acronym pcap (usually written in lower case) stands
|
|
for @emph{p}acket @emph{cap}ture, and is actually an API that includes the
|
|
definition of a @code{.pcap} file format. The most popular program that can
|
|
read and display this format is Wireshark (formerly called Ethereal).
|
|
|
|
If you are unfamilar with Wireshark, there is a web site available from which
|
|
you can download programs and documentation: @uref{http://www.wireshark.org/}.
|
|
|
|
@cindex csma-echo-ascii-trace.cc
|
|
@cindex csma-echo-pcap-trace.cc
|
|
The code used to enable pcap tracing is similar to that for ASCII tracing.
|
|
We have provided another file, @code{csma-echo-pcap-trace.cc} that uses the
|
|
pcap trace wrapper. We have added the code to include the pcap trace wrapper
|
|
defintions:
|
|
|
|
@verbatim
|
|
#include "ns3/pcap-trace.h"
|
|
@end verbatim
|
|
|
|
And then added the following code below the AsciiTrace methods:
|
|
|
|
@cindex PcapTrace
|
|
@cindex PcapTrace!TraceAllIp
|
|
@verbatim
|
|
PcapTrace pcaptrace ("tutorial.pcap");
|
|
pcaptrace.TraceAllIp ();
|
|
@end verbatim
|
|
|
|
The first line of the code immediately above declares an object of type
|
|
@code{PcapTrace} named @code{pcaptrace} and passes a string parameter to its
|
|
constructor. This object is used to hide the details of the actual tracing
|
|
subsystem. The parameter is a base file name from which the actual trace file
|
|
names will be built. The second line of code tells the @code{PcamTrace}
|
|
object to trace all IP activity in all of the nodes present in the simulation.
|
|
|
|
@cindex interface index
|
|
Trace files are not created until trace activity is detected. Each file name
|
|
is composed of the base file name, followed by a @code{'-'}, a node id followed
|
|
by a @code{'-}', and an IP interface index. You will soon see a file named
|
|
@code{tutorial.pcap-0-1}, for example. This will be the trace file generated
|
|
as events are detected on node zero, interface index one. N.B. Interface
|
|
indices are different that net device indices --- interface index zero
|
|
corresponds to the loopback interface and interface index one corresponds to
|
|
the first net device you added to a node.
|
|
|
|
You may run the new program just like all of the others so far:
|
|
|
|
@cindex Waf
|
|
@verbatim
|
|
./waf --run csma-echo-pcap-trace
|
|
@end verbatim
|
|
|
|
If you look at the top level directory of your distribution, you should now
|
|
see three log files: @code{tutorial.tr} is the ASCII trace file we have
|
|
previously examined. @code{tutorial.pcap-0-1} and @code{tutorial.pcap-1-1}
|
|
are the new pcap files we just generated. There will not be files
|
|
corresponding to nodes two and three since we have not sent any IP packets to
|
|
those nodes.
|
|
|
|
@cindex Wireshark
|
|
If you have Wireshark available, you can open each of the trace files and
|
|
display the contents as if you had captured the packets using a
|
|
@emph{packet sniffer}. Note that only IP packets are traced using this
|
|
wrapper, so you will not see the ARP exchanges that were logged when using
|
|
the ASCII trace wrapper. You are encouraged to take a look at the contents
|
|
of these pcap files using your favorite pcap software (or Wireshark).
|
|
|
|
@c ========================================================================
|
|
@c Other Network Topologies
|
|
@c ========================================================================
|
|
|
|
@node Other-network-topologies
|
|
@chapter Other Network Topologies
|
|
@cindex topology
|
|
@cindex Channel
|
|
@cindex NetDevice
|
|
@cindex topology!bus
|
|
@cindex topology!point-to-point
|
|
@cindex PointToPointChannel
|
|
@cindex PointToPointNetDevice
|
|
|
|
@emph{Network topology} is the study of the arrangement of of the elements
|
|
(in @command{ns-3} represented by the classes @code{Channel} and @code{Node})
|
|
of a network. Two fundamental types of physical topologies are the
|
|
@emph{point-to-point} and @emph{bus} topologies. We have already been exposed
|
|
to the @command{ns-3} channel specialization named @code{CsmaChannel}. This is
|
|
a simulation of a bus network. We also provide a simulation of a
|
|
point-to-point channel with associated net devices. As described previously,
|
|
the associated C++ classes specialize the @command{ns-3} base classes
|
|
@code{NetDevice} and @code{Channel} and are called @code{PointToPointNetDevice}
|
|
and @code{PointToPointChannel} respectively.
|
|
|
|
We will use combinations of these bus and point-to-point topology elements
|
|
to show how to create several commonly seen network topologies.
|
|
|
|
@section A Point-to-Point Network
|
|
We're going to take what might be seen as a step backward and look at a simple
|
|
point-to-point network. We will be building the simplest network you can
|
|
imagine. A serial link (point to point) between two computers. When you
|
|
see this point-to-point network, you can think of an RS-422 (or RS-232 for
|
|
you old-timers) cable. This topology is shown below.
|
|
|
|
@sp 1
|
|
@center @image{pp,,,,png}
|
|
|
|
We have provided a file for you in the @code{tutorial}
|
|
directory called @code{point-to-point.cc}. You should now be familiar enough
|
|
with the system to pick out fairly easily what has been changed. Let's focus
|
|
on the following lines:
|
|
|
|
@verbatim
|
|
Ptr<Node> n0 = Create<InternetNode> ();
|
|
Ptr<Node> n1 = Create<InternetNode> ();
|
|
|
|
Ptr<PointToPointChannel> link = PointToPointTopology::AddPointToPointLink (
|
|
n0, n1, DataRate (38400), MilliSeconds (20));
|
|
|
|
PointToPointTopology::AddIpv4Addresses (link, n0, "10.1.1.1",
|
|
n1, "10.1.1.2");
|
|
@end verbatim
|
|
|
|
You can see that we created two @code{InternetNode} objects in the usual way.
|
|
Then, instead of creating a @code{CsmaChannel} we create a
|
|
@code{PointToPointChannel}. This point-to-point channel, which we call
|
|
@code{link}, connects node zero (@code{n0}) and node one (@code{n1}) over a
|
|
simulated link that runs at 38400 bits per second and has a 20 millisecond
|
|
simulated speed-of-light delay. This call also creates appropriate net devices
|
|
and attaches them to nodes zero and one.
|
|
|
|
We then add IP addresses to the net devices we just created using the topology
|
|
helper @code{AddIpv4Addresses}. Node zero gets the IP address 10.1.1.1 and
|
|
node one gets the IP address 10.1.1.2 assigned.
|
|
|
|
The alert tutorial user may wonder what the network number or prefix is of
|
|
those IP addresses. The point-to-point topology assumes that you want a
|
|
@code{/30} subnet and assigns an appropriate net mask for you. It then then
|
|
@emph{asserts} that the network numbers of the two net devices match. So there
|
|
is an implicit network mask created down in the topology code that looks like,
|
|
|
|
@verbatim
|
|
Ipv4Mask netmask("255.255.255.252");
|
|
@end verbatim
|
|
|
|
The rest of the code you should recognize and understand. We are just going
|
|
to echo one packet across the point-to-point link. You should be now be able
|
|
to build and run this example and to locate and interpret the ASCII trace
|
|
file. This is left as an exercise for you.
|
|
|
|
The file @code{point-to-point.cc} is reproduced here for your
|
|
convenience:
|
|
|
|
@verbatim
|
|
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
/*
|
|
* 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
|
|
*/
|
|
|
|
#include "ns3/log.h"
|
|
#include "ns3/ptr.h"
|
|
#include "ns3/internet-node.h"
|
|
#include "ns3/point-to-point-channel.h"
|
|
#include "ns3/mac48-address.h"
|
|
#include "ns3/point-to-point-net-device.h"
|
|
#include "ns3/point-to-point-topology.h"
|
|
#include "ns3/udp-echo-client.h"
|
|
#include "ns3/udp-echo-server.h"
|
|
#include "ns3/simulator.h"
|
|
#include "ns3/nstime.h"
|
|
#include "ns3/ascii-trace.h"
|
|
#include "ns3/pcap-trace.h"
|
|
#include "ns3/global-route-manager.h"
|
|
|
|
NS_LOG_COMPONENT_DEFINE ("PointToPointSimulation");
|
|
|
|
using namespace ns3;
|
|
|
|
// Network topology
|
|
//
|
|
// point to point
|
|
// +--------------+
|
|
// | |
|
|
// n0 n1
|
|
//
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
LogComponentEnable ("PointToPointSimulation", LOG_LEVEL_INFO);
|
|
|
|
NS_LOG_INFO ("Point to Point Topology Simulation");
|
|
|
|
Ptr<Node> n0 = Create<InternetNode> ();
|
|
Ptr<Node> n1 = Create<InternetNode> ();
|
|
|
|
Ptr<PointToPointChannel> link = PointToPointTopology::AddPointToPointLink (
|
|
n0, n1, DataRate (38400), MilliSeconds (20));
|
|
|
|
PointToPointTopology::AddIpv4Addresses (link, n0, "10.1.1.1",
|
|
n1, "10.1.1.2");
|
|
|
|
uint16_t port = 7;
|
|
|
|
Ptr<UdpEchoClient> client = Create<UdpEchoClient> (n0, "10.1.1.2", port,
|
|
1, Seconds(1.), 1024);
|
|
|
|
Ptr<UdpEchoServer> server = Create<UdpEchoServer> (n1, port);
|
|
|
|
server->Start(Seconds(1.));
|
|
client->Start(Seconds(2.));
|
|
|
|
server->Stop (Seconds(10.));
|
|
client->Stop (Seconds(10.));
|
|
|
|
AsciiTrace asciitrace ("tutorial.tr");
|
|
asciitrace.TraceAllQueues ();
|
|
asciitrace.TraceAllNetDeviceRx ();
|
|
|
|
Simulator::Run ();
|
|
Simulator::Destroy ();
|
|
}
|
|
@end verbatim
|
|
|
|
@section A Star Network
|
|
A point-to-point network is considered a special case of a star network. As
|
|
you might expect, the process of constructing a star network is an extension
|
|
of the very simple process used for a point-to-point link. We have provided
|
|
a file for you in the @code{tutorial} directory called @code{star.cc}
|
|
that implements a simple star network as seen below.
|
|
|
|
@sp 1
|
|
@center @image{star,,,,png}
|
|
|
|
In order to create a star network, we need to be able to instantiate some
|
|
number (greater than one) of net devices on a node. In the name of simplicity
|
|
of use, the @code{PointToPointTopology} topology helper does not allow one to
|
|
do this. We provided a separate topology helper class, the
|
|
@code{PointToPointIpv4Topology} helper class that provides the slightly finer
|
|
granularity we need to accomplish a star network. In order to use this new
|
|
helper we have to load the definitions by including the appropriate file.
|
|
|
|
@verbatim
|
|
#include "ns3/point-to-point-ipv4-topology.h"
|
|
@end verbatim
|
|
|
|
The star that we're going to create has a node in the center (@code{n0}) with
|
|
six nodes surrounding (@code{n1} - @code{n6}). You should be able to easily
|
|
find and understand the code that creates these nodes.
|
|
|
|
@verbatim
|
|
Ptr<Node> n0 = Create<InternetNode> ();
|
|
Ptr<Node> n1 = Create<InternetNode> ();
|
|
Ptr<Node> n2 = Create<InternetNode> ();
|
|
Ptr<Node> n3 = Create<InternetNode> ();
|
|
Ptr<Node> n4 = Create<InternetNode> ();
|
|
Ptr<Node> n5 = Create<InternetNode> ();
|
|
Ptr<Node> n6 = Create<InternetNode> ();
|
|
@end verbatim
|
|
|
|
Next, we get into the differences between the @code{PointToPointTopology}
|
|
helper and the @code{PointToPointIpv4Topology} helper. The
|
|
@code{PointToPointIpv4Topology} helper looks and feels a little like the
|
|
@code{CsmaIpv4Topology} helper. Just like you created a CSMA channel
|
|
previously, you need to create a point-to-point channel. The following
|
|
code creates a @code{PointToPointChannel} and calls it @code{link01}. You can
|
|
interpret this name as being the channel (or @emph{link}) from node zero to
|
|
node one.
|
|
|
|
@verbatim
|
|
Ptr<PointToPointChannel> link01 =
|
|
PointToPointIpv4Topology::CreateChannel (DataRate (38400),
|
|
MilliSeconds (20));
|
|
@end verbatim
|
|
|
|
You need to provide a data rate for the channel which we set at 38400 bits
|
|
per second. You must also provide a speed-of-light delay which we set at
|
|
20 milliseconds.
|
|
|
|
Just as you added a net device to the nodes in the CSMA tutorial section, you
|
|
do the same here but with a point-to-point net device. The following code
|
|
illustrates how we do that:
|
|
|
|
@verbatim
|
|
uint32_t nd01 = PointToPointIpv4Topology::AddNetDevice (n0,
|
|
link01);
|
|
@end verbatim
|
|
|
|
We call the @code{PointToPointIpv4Topology} helper and ask it to add a net
|
|
device to node zero (@code{n0}) and connect it to the appropriate
|
|
point-to-point link (@code{link01}) which you will recall is the serial link
|
|
from node zero to node one.
|
|
|
|
If you look at the following code, you will see the same calls are repeated
|
|
to create the remaining five point-to-point channels and connect them
|
|
to net devices on node zero.
|
|
|
|
The next new code is found after the ``spokes'' of the star have been created.
|
|
It looks like the following:
|
|
|
|
@verbatim
|
|
uint32_t nd1 = PointToPointIpv4Topology::AddNetDevice (n1, link01);
|
|
uint32_t nd2 = PointToPointIpv4Topology::AddNetDevice (n2, link02);
|
|
uint32_t nd3 = PointToPointIpv4Topology::AddNetDevice (n3, link03);
|
|
uint32_t nd4 = PointToPointIpv4Topology::AddNetDevice (n4, link04);
|
|
uint32_t nd5 = PointToPointIpv4Topology::AddNetDevice (n5, link05);
|
|
uint32_t nd6 = PointToPointIpv4Topology::AddNetDevice (n6, link06);
|
|
@end verbatim
|
|
|
|
Here we are creating the net devices on the nodes surrounding the center node.
|
|
In the first call, we are adding a net device on node one (@code{n1}) and
|
|
connecting that net device to the channel named @code{link01}. Remember that
|
|
we created the channel @code{link01} as the channel connecting node zero and
|
|
node one. We previously created a net device on node zero and attached that
|
|
device to @code{link01}. Here we are connecting the other side of that link
|
|
to node one. The return value from this call is the net device index of the
|
|
created net device.
|
|
|
|
The next section of code adds addresses to the net devices we just created.
|
|
The first call adds the IP address 10.1.1.1 to the net device going from
|
|
node zero to node one. Recall that we first created a node named @code{n0}
|
|
and a channel called @code{link01}. We added a net device to @code{n0} and
|
|
remembered the net device index as the @code{uint32_t nd01}. This meant
|
|
the net device @emph{nd} on node @emph{0} that we connected to node @emph{1}.
|
|
We call @code{AddAddress} to add an IP address (10.1.1.1) to the net device
|
|
on node zero identified by the net device index @code{nd01}. We provide a
|
|
net mask suitable for a point to point network. This is typically a /30
|
|
address but we don't force that in this API.
|
|
|
|
After setting up the address on node zero, we do the same for the node on
|
|
the other end of the ``spoke'' --- in this case node one, with its single
|
|
net device. Note that the network number is the same on both sides of this
|
|
network.
|
|
|
|
@verbatim
|
|
PointToPointIpv4Topology::AddAddress (n0, nd01, "10.1.1.1",
|
|
``255.255.255.252'');
|
|
|
|
PointToPointIpv4Topology::AddAddress (n1, nd1, "10.1.1.2",
|
|
``255.255.255.252'');
|
|
@end verbatim
|
|
|
|
The following code repeats this pattern assining similar IP addresses to the
|
|
remaining net devices. Note that there are no @code{Mac48Address} address
|
|
assignments --- they are not required.
|
|
|
|
The rest of the code you should recognize and understand. We are just going
|
|
to echo one packet across the point-to-point link. You should be now be able
|
|
to build and run this example and to locate and interpret the ASCII trace
|
|
file. This is left as an exercise for you.
|
|
|
|
The file @code{star.cc} is reproduced here for your convenience:
|
|
|
|
@verbatim
|
|
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
/*
|
|
* 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
|
|
*/
|
|
|
|
#include "ns3/log.h"
|
|
#include "ns3/ptr.h"
|
|
#include "ns3/internet-node.h"
|
|
#include "ns3/point-to-point-channel.h"
|
|
#include "ns3/mac48-address.h"
|
|
#include "ns3/point-to-point-net-device.h"
|
|
#include "ns3/point-to-point-ipv4-topology.h"
|
|
#include "ns3/udp-echo-client.h"
|
|
#include "ns3/udp-echo-server.h"
|
|
#include "ns3/simulator.h"
|
|
#include "ns3/nstime.h"
|
|
#include "ns3/ascii-trace.h"
|
|
#include "ns3/pcap-trace.h"
|
|
#include "ns3/global-route-manager.h"
|
|
|
|
NS_LOG_COMPONENT_DEFINE ("StarSimulation");
|
|
|
|
using namespace ns3;
|
|
|
|
// Network topology
|
|
//
|
|
// n3 n2
|
|
// | /
|
|
// | /
|
|
// n4 --- n0 --- n1
|
|
// / |
|
|
// / |
|
|
// n5 n6
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
LogComponentEnable ("StarSimulation", LOG_LEVEL_INFO);
|
|
|
|
NS_LOG_INFO ("Star Topology Simulation");
|
|
|
|
Ptr<Node> n0 = Create<InternetNode> ();
|
|
Ptr<Node> n1 = Create<InternetNode> ();
|
|
Ptr<Node> n2 = Create<InternetNode> ();
|
|
Ptr<Node> n3 = Create<InternetNode> ();
|
|
Ptr<Node> n4 = Create<InternetNode> ();
|
|
Ptr<Node> n5 = Create<InternetNode> ();
|
|
Ptr<Node> n6 = Create<InternetNode> ();
|
|
|
|
Ptr<PointToPointChannel> link01 =
|
|
PointToPointIpv4Topology::CreateChannel (DataRate (38400),
|
|
MilliSeconds (20));
|
|
|
|
uint32_t nd01 = PointToPointIpv4Topology::AddNetDevice (n0,
|
|
link01);
|
|
|
|
Ptr<PointToPointChannel> link02 =
|
|
PointToPointIpv4Topology::CreateChannel (DataRate (38400),
|
|
MilliSeconds (20));
|
|
|
|
uint32_t nd02 = PointToPointIpv4Topology::AddNetDevice (n0,
|
|
link02);
|
|
|
|
Ptr<PointToPointChannel> link03 =
|
|
PointToPointIpv4Topology::CreateChannel (DataRate (38400),
|
|
MilliSeconds (20));
|
|
|
|
uint32_t nd03 = PointToPointIpv4Topology::AddNetDevice (n0,
|
|
link03);
|
|
|
|
Ptr<PointToPointChannel> link04 =
|
|
PointToPointIpv4Topology::CreateChannel (DataRate (38400),
|
|
MilliSeconds (20));
|
|
|
|
uint32_t nd04 = PointToPointIpv4Topology::AddNetDevice (n0,
|
|
link04);
|
|
|
|
Ptr<PointToPointChannel> link05 =
|
|
PointToPointIpv4Topology::CreateChannel (DataRate (38400),
|
|
MilliSeconds (20));
|
|
|
|
uint32_t nd05 = PointToPointIpv4Topology::AddNetDevice (n0,
|
|
link05);
|
|
|
|
Ptr<PointToPointChannel> link06 =
|
|
PointToPointIpv4Topology::CreateChannel (DataRate (38400),
|
|
MilliSeconds (20));
|
|
|
|
uint32_t nd06 = PointToPointIpv4Topology::AddNetDevice (n0, link06);
|
|
|
|
uint32_t nd1 = PointToPointIpv4Topology::AddNetDevice (n1, link01);
|
|
uint32_t nd2 = PointToPointIpv4Topology::AddNetDevice (n2, link02);
|
|
uint32_t nd3 = PointToPointIpv4Topology::AddNetDevice (n3, link03);
|
|
uint32_t nd4 = PointToPointIpv4Topology::AddNetDevice (n4, link04);
|
|
uint32_t nd5 = PointToPointIpv4Topology::AddNetDevice (n5, link05);
|
|
uint32_t nd6 = PointToPointIpv4Topology::AddNetDevice (n6, link06);
|
|
|
|
PointToPointIpv4Topology::AddAddress (n0, nd01, "10.1.1.1",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n1, nd1, "10.1.1.2",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n0, nd02, "10.1.2.1",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n2, nd2, "10.1.2.2",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n0, nd03, "10.1.3.1",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n3, nd3, "10.1.2.2",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n0, nd04, "10.1.4.1",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n4, nd4, "10.1.4.2",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n0, nd05, "10.1.5.1",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n5, nd5, "10.1.5.2",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n0, nd06, "10.1.6.1",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n6, nd6, "10.1.6.2",
|
|
"255.255.255.252");
|
|
|
|
uint16_t port = 7;
|
|
|
|
Ptr<UdpEchoClient> client = Create<UdpEchoClient> (n0, "10.1.1.2", port,
|
|
1, Seconds(1.), 1024);
|
|
|
|
Ptr<UdpEchoServer> server = Create<UdpEchoServer> (n1, port);
|
|
|
|
server->Start(Seconds(1.));
|
|
client->Start(Seconds(2.));
|
|
|
|
server->Stop (Seconds(10.));
|
|
client->Stop (Seconds(10.));
|
|
|
|
AsciiTrace asciitrace ("tutorial.tr");
|
|
asciitrace.TraceAllQueues ();
|
|
asciitrace.TraceAllNetDeviceRx ();
|
|
|
|
Simulator::Run ();
|
|
Simulator::Destroy ();
|
|
}
|
|
@end verbatim
|
|
|
|
@subsection Routing
|
|
If you are really excited about this simulator you may have already tried to
|
|
modify the scripts outside the tutorial. I know that one of the first things
|
|
that would have occurred to me when I saw the star network would have been to
|
|
start trying to add applications to echo packets from nodes other than zero.
|
|
If you tried, for example, to start the echo client on node one instead of
|
|
node zero, you would have found an empty trace file. The reason for this
|
|
is that you have now created an internetwork. This means you will need to
|
|
enable internetwork routing.
|
|
|
|
We have provided a file for you in the @code{tutorial} directory called
|
|
@code{star-routing.cc} to show you how this is done. This extremely
|
|
tricky and difficult change is shown below:
|
|
|
|
@verbatim
|
|
GlobalRouteManager::PopulateRoutingTables ();
|
|
@end verbatim
|
|
|
|
This one-line addition, located just before the simulation runs, tells the
|
|
@command{ns-3} @emph{global route manager} to walk the topology you created and
|
|
build internetwork routing tables for all of the nodes in the simulation.
|
|
We changed the client application so that it runs on node four:
|
|
|
|
@verbatim
|
|
Ptr<UdpEchoClient> client = Create<UdpEchoClient> (n4, "10.1.1.2", port,
|
|
1, Seconds(1.), 1024);
|
|
@end verbatim
|
|
|
|
Now if you build and run @code{star-routing.cc} you can examine the
|
|
@code{tutorial.tr} file and see that your UDP echo packets are now correctly
|
|
routed through the topology.
|
|
|
|
@section A Dumbbell Network
|
|
One of the most interesting simple topologies (from a phenomenological point of
|
|
view) is commonly called a dumbbell network. The name derives from a
|
|
superficial similarity in form to a piece of exercise equipment.
|
|
|
|
The dumbbell model is typically composed of two bus or star network elements
|
|
connected via a point-to-point link. The point-to-point link is usually
|
|
configured with a lower bandwidth than the bus elements to provide a
|
|
@emph{choke point}.
|
|
|
|
The following is a representation of the topology.
|
|
|
|
@sp 1
|
|
@center @image{dumbbell,,,,png}
|
|
|
|
We have provided a file that constructs this dumbbell network and creates
|
|
enough data flowing across the choke point that some packets will be dropped.
|
|
The file is called @code{linear-dumbbell.cc} and is located in the
|
|
@code{tutorial} directory. We have already covered all of the code used to
|
|
create this network, so we will just quickly go over the main sections of the
|
|
script.
|
|
|
|
The first section creates a CSMA lan that will become the left side of the
|
|
dumbbell network. This code should be very familiar since we used the same
|
|
process to create our first example.
|
|
|
|
@verbatim
|
|
//
|
|
// Create the lan on the left side of the dumbbell.
|
|
//
|
|
Ptr<Node> n0 = Create<InternetNode> ();
|
|
Ptr<Node> n1 = Create<InternetNode> ();
|
|
Ptr<Node> n2 = Create<InternetNode> ();
|
|
Ptr<Node> n3 = Create<InternetNode> ();
|
|
|
|
Ptr<CsmaChannel> lan1 =
|
|
CsmaTopology::CreateCsmaChannel (DataRate (10000000), MilliSeconds (2));
|
|
|
|
uint32_t nd0 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n0, lan1,
|
|
"08:00:2e:00:00:00");
|
|
|
|
uint32_t nd1 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n1, lan1,
|
|
"08:00:2e:00:00:01");
|
|
|
|
uint32_t nd2 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n2, lan1,
|
|
"08:00:2e:00:00:02");
|
|
|
|
uint32_t nd3 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n3, lan1,
|
|
"08:00:2e:00:00:03");
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n0, nd0, "10.1.1.1", "255.255.255.0");
|
|
CsmaIpv4Topology::AddIpv4Address (n1, nd1, "10.1.1.2", "255.255.255.0");
|
|
CsmaIpv4Topology::AddIpv4Address (n2, nd2, "10.1.1.3", "255.255.255.0");
|
|
CsmaIpv4Topology::AddIpv4Address (n3, nd3, "10.1.1.4", "255.255.255.0");
|
|
@end verbatim
|
|
|
|
The code to generate the CSMA lan on the right side is similar; only the names
|
|
have been changed.
|
|
|
|
@verbatim
|
|
//
|
|
// Create the lan on the right side of the dumbbell.
|
|
//
|
|
Ptr<Node> n4 = Create<InternetNode> ();
|
|
Ptr<Node> n5 = Create<InternetNode> ();
|
|
Ptr<Node> n6 = Create<InternetNode> ();
|
|
Ptr<Node> n7 = Create<InternetNode> ();
|
|
|
|
Ptr<CsmaChannel> lan2 =
|
|
CsmaTopology::CreateCsmaChannel (DataRate (10000000), MilliSeconds (2));
|
|
|
|
uint32_t nd4 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n4, lan2,
|
|
"08:00:2e:00:00:04");
|
|
|
|
uint32_t nd5 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n5, lan2,
|
|
"08:00:2e:00:00:05");
|
|
|
|
uint32_t nd6 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n6, lan2,
|
|
"08:00:2e:00:00:06");
|
|
|
|
uint32_t nd7 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n7, lan2,
|
|
"08:00:2e:00:00:07");
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n4, nd4, "10.1.2.1", "255.255.255.0");
|
|
CsmaIpv4Topology::AddIpv4Address (n5, nd5, "10.1.2.2", "255.255.255.0");
|
|
CsmaIpv4Topology::AddIpv4Address (n6, nd6, "10.1.2.3", "255.255.255.0");
|
|
CsmaIpv4Topology::AddIpv4Address (n7, nd7, "10.1.2.4", "255.255.255.0");
|
|
@end verbatim
|
|
|
|
Next, we create a point to point link to connect the two lans. We connect
|
|
the point-to-point channel between nodes three (on the left lan) and four
|
|
(on the right lan). You should recoginze this as substantially similar to
|
|
the link setup from the @code{point-to-point} example.
|
|
|
|
@verbatim
|
|
//
|
|
// Create the point-to-point link to connect the two lans.
|
|
//
|
|
Ptr<PointToPointChannel> link = PointToPointTopology::AddPointToPointLink (
|
|
n3, n4, DataRate (38400), MilliSeconds (20));
|
|
|
|
PointToPointTopology::AddIpv4Addresses (link, n3, "10.1.3.1",
|
|
n4, "10.1.3.2");
|
|
@end verbatim
|
|
|
|
Then we configure data flows. We create four echo clients that send UDP
|
|
packets from the left side lan to servers created on the right side lan.
|
|
Notice that we send 100 packets with an inter-packet gap of ten milliseconds
|
|
instead of the single packet we have previously used. This data rate is
|
|
sufficient to saturate the point-to-point link and will cause packets to be
|
|
dropped when the queue on the link net devices overflows (the default maximum
|
|
queue depth is 100 packets). Note that we stagger the start of the echo
|
|
clients to slowly bring up the data rates.
|
|
|
|
@verbatim
|
|
//
|
|
// Create data flows across the link:
|
|
// n0 ==> n4 ==> n0
|
|
// n1 ==> n5 ==> n1
|
|
// n2 ==> n6 ==> n2
|
|
// n3 ==> n7 ==> n3
|
|
//
|
|
uint16_t port = 7;
|
|
|
|
Ptr<UdpEchoClient> client0 = Create<UdpEchoClient> (n0, "10.1.2.1", port,
|
|
100, Seconds(.01), 1024);
|
|
Ptr<UdpEchoClient> client1 = Create<UdpEchoClient> (n1, "10.1.2.2", port,
|
|
100, Seconds(.01), 1024);
|
|
Ptr<UdpEchoClient> client2 = Create<UdpEchoClient> (n2, "10.1.2.3", port,
|
|
100, Seconds(.01), 1024);
|
|
Ptr<UdpEchoClient> client3 = Create<UdpEchoClient> (n3, "10.1.2.4", port,
|
|
100, Seconds(.01), 1024);
|
|
|
|
Ptr<UdpEchoServer> server4 = Create<UdpEchoServer> (n4, port);
|
|
Ptr<UdpEchoServer> server5 = Create<UdpEchoServer> (n5, port);
|
|
Ptr<UdpEchoServer> server6 = Create<UdpEchoServer> (n6, port);
|
|
Ptr<UdpEchoServer> server7 = Create<UdpEchoServer> (n7, port);
|
|
|
|
server4->Start(Seconds(1.));
|
|
server5->Start(Seconds(1.));
|
|
server6->Start(Seconds(1.));
|
|
server7->Start(Seconds(1.));
|
|
|
|
client0->Start(Seconds(2.));
|
|
client1->Start(Seconds(2.1));
|
|
client2->Start(Seconds(2.2));
|
|
client3->Start(Seconds(2.3));
|
|
|
|
server4->Stop (Seconds(10.));
|
|
server5->Stop (Seconds(10.));
|
|
server6->Stop (Seconds(10.));
|
|
server7->Stop (Seconds(10.));
|
|
|
|
client0->Stop (Seconds(10.));
|
|
client1->Stop (Seconds(10.));
|
|
client2->Stop (Seconds(10.));
|
|
client3->Stop (Seconds(10.));
|
|
@end verbatim
|
|
|
|
The remainder of the file should be quite familiar to you. Go ahead and
|
|
run @code{linear-dumbbell}. Now take a look at the trace (@code{tutorial.tr})
|
|
file. You will now see trace lines that begin with @code{d}. Alternatively
|
|
you can search for the string ``queue-drop'' which is the expansion of the
|
|
drop code.
|
|
|
|
Interpretation of a dropped packet is straightforward. We have expanded
|
|
the first @code{queue-drop} trace for you below. See the section on ASCII
|
|
tracing for details.
|
|
|
|
@verbatim
|
|
00 d
|
|
01 2.40938
|
|
02 nodeid=3
|
|
03 device=1
|
|
04 queue-drop
|
|
05 pkt-uid=124
|
|
06 LLCSNAP(type 0x800)
|
|
07 IPV4(
|
|
08 tos 0x0
|
|
09 ttl 63
|
|
10 id 20
|
|
11 offset 0
|
|
12 flags [none]
|
|
13 length: 1052) 10.1.1.3 > 10.1.2.3
|
|
14 UDP(length: 1032)
|
|
15 49153 > 7
|
|
16 DATA (length 1024)
|
|
@end verbatim
|
|
|
|
We leave it as an exercise to examine the trace files in more detail.
|
|
|
|
@c ========================================================================
|
|
@c Nonlinear Thinking
|
|
@c ========================================================================
|
|
|
|
@node Nonlinear-Thinking
|
|
@chapter Nonlinear Thinking
|
|
|
|
One thing that all of our examples so far have in common is that they are
|
|
composed of a linear collection of calls into the @command{ns-3} system. The
|
|
programmers among the readers may have wondered why there is not as much
|
|
as a for-loop in all of the examples. The answer is that we wanted to
|
|
introduce you to @command{ns-3} scripting with a minimum of conceptual
|
|
overhead. We're going to remedy that situation shortly.
|
|
|
|
We have written a number of @command{ns-3} scripts in C++. Although we have
|
|
been perfectly linear in our script implementations, just like any other C++
|
|
program, an @command{ns-3} script can use any features of the language you
|
|
desire. If you will look back at the @code{linear-dumbbell.cc}
|
|
example, you may notice that the code to create the left and right sides of
|
|
the dumbbell is operationally identical --- only the names change. An obvious
|
|
improvement of this program would be to use subroutines to create the sides.
|
|
Since we are working with C++, we should probably do this in an
|
|
object-oriented way. Since object-oriented design is somewhat of a black art
|
|
to some people, we'll take some time here and outline a simple methodology
|
|
you can follow.
|
|
|
|
@section Object Design 101 --- Class Ipv4BusNetwork
|
|
If you are a master of object oriented design, feel free to skip or skim this
|
|
section, in which we derive a simplistic but fully operational bus network
|
|
class.
|
|
|
|
So you want to create a BusNetwork class. Often the biggest hurdle in a
|
|
design is figuring out how to get started. One of the simplest and most
|
|
straightforward ways to do an object decomposition of a problem is to simply
|
|
write down a description of the problem and take a look at the words
|
|
you used. Let's take some time and do that, first at a very high level.
|
|
|
|
@example
|
|
A bus network is an implementation of a particular network topology that
|
|
contains some number of nodes. Each of these nodes is attached to a single
|
|
multi-drop channel. The network itself has some attributes independent of
|
|
the topology such as a network mask, network number (prefix) and base IP
|
|
address.
|
|
@end example
|
|
|
|
The first thing to do is to focus on the nouns and adjectives. These will
|
|
give you a starting point for required classes and member variables.
|
|
|
|
Immediately we can notice that at the highest level we are talking about the
|
|
noun @emph{network}. This probably won't surprise you. We also have an
|
|
adjective that modifies the noun --- @emph{bus}. This should lead us to our
|
|
first class defintion. Usually class names are constructed in the same way
|
|
as an English language sentence would be spoken. For example, one would speak
|
|
of a @emph{bus network} in conversation, so we would normally create a
|
|
@code{class BusNetwork} to represent it.
|
|
|
|
One thing to note is that we have used two words in our description quite
|
|
naturally: @emph{is} and @emph{has}. When you see these words should should
|
|
immediately think of the object-oriented concepts of @emph{ISA} (inheritance)
|
|
and @emph{HASA} (containment) respectively. We wrote that a bus network
|
|
@emph{is} an implementation of a particular network topology. Perhaps you
|
|
will agree that there is a natural base class called @code{Network} that
|
|
@emph{has} the attributes discussed above. The fact that a @code{BusNetwork}
|
|
@emph{ISA} kind of @code{Network} suggests inheritance. Let's capture that
|
|
thought right away remembering that we're focused on IP version four here:
|
|
|
|
@verbatim
|
|
class Ipv4Network
|
|
{
|
|
public:
|
|
Ipv4Address m_network;
|
|
Ipv4Mask m_mask;
|
|
Ipv4Address m_baseAddress;
|
|
};
|
|
|
|
class Ipv4BusNetwork : public Ipv4Network
|
|
{
|
|
};
|
|
@end verbatim
|
|
|
|
Let's take a look at the @emph{HASA} relationships of the bus network. Clearly
|
|
it will @emph{have} a reference to the underlying channel that implements the
|
|
actual communications medium. We use smart pointers for those references, so
|
|
one member variable is obvious:
|
|
|
|
@verbatim
|
|
Ptr<CsmaChannel> m_channel;
|
|
@end verbatim
|
|
|
|
A bus network will also need to contain references to all of the nodes we
|
|
eventually want to create. If you are working in C++ and see the words contain
|
|
or container, you should immediately think of the Standard Template Library
|
|
or STL. A quick search of the available containers there will probably lead
|
|
you to consider the vector class. A vector is a container that looks like an
|
|
array. This is just what we need here. Again, we want to use smart pointers
|
|
to reference our nodes, so the declaration of the vector would look like,
|
|
|
|
@verbatim
|
|
std::vector<Ptr<Node> > m_nodes;
|
|
@end verbatim
|
|
|
|
It will save you headaches in the future if you notice that the space between
|
|
the two right brackets is required to differentiate this situation from a
|
|
right-shift operator. So we have a pretty good start already after just a
|
|
little work. Now we need to turn our attention to actions. Let's write
|
|
another little description of the things you consider doing to a Bus network.
|
|
|
|
@example
|
|
We need to be able to create a bus network. We need to be able to delete a
|
|
bus network. We need to be able to get a handle to a node in order to add
|
|
applications. We need to be able to set the network, mask and base address
|
|
somehow, specify how many nodes to create and provide the underlying channel
|
|
its required bandwidth and delay parameters.
|
|
@end example
|
|
|
|
We now look at the @emph{verbs} in that sentence. These will give a good
|
|
starting point for the methods of the classes. For example, the verbs
|
|
@emph{create} and @emph{delete} should suggest @emph{constructor} and
|
|
@emph{destructor}. The verb @emph{get} leads us to providing a method called
|
|
@code{GetNode}. We have to provide a number of parameters so we can either
|
|
provide @emph{setters} or we can simply pass them in as parameters to our
|
|
constructors. Since this is a simple example, we won't bother to implement
|
|
getters and setters (methods to get and set member variables to enhance data
|
|
hiding). Let's use this guidance to finish up our class declarations:
|
|
|
|
@verbatim
|
|
class Ipv4Network
|
|
{
|
|
public:
|
|
Ipv4Network (Ipv4Address network, Ipv4Mask mask, Ipv4Address address);
|
|
virtual ~Ipv4Network ();
|
|
|
|
Ipv4Address m_network;
|
|
Ipv4Mask m_mask;
|
|
Ipv4Address m_baseAddress;
|
|
};
|
|
|
|
class Ipv4BusNetwork : public Ipv4Network
|
|
{
|
|
public:
|
|
Ipv4BusNetwork (
|
|
Ipv4Address network,
|
|
Ipv4Mask mask,
|
|
Ipv4Address startAddress,
|
|
DataRate bps,
|
|
Time delay,
|
|
uint32_t n);
|
|
|
|
virtual ~Ipv4BusNetwork ();
|
|
|
|
Ptr<Node> GetNode (uint32_t n);
|
|
|
|
private:
|
|
std::vector<Ptr<Node> > m_nodes;
|
|
Ptr<CsmaChannel> m_channel;
|
|
};
|
|
@end verbatim
|
|
|
|
That's it. We have actually already walked through almost all of the code
|
|
required to construct a bus network in our @code{csma-echo.cc}
|
|
example, so let's just jump forward and take a look at an implementation
|
|
of this thing. We provide an implementation for you in the files
|
|
@code{ipv4-bus-network.h} and @code{ipv4-bus-network.cc} located in the
|
|
@code{tutorial} directory. We also provide an example that uses the new
|
|
class in the file @code{bus-network.cc}.
|
|
|
|
The interesting method from our current perspective is the Ipv4BusNetwork
|
|
constructor, shown below:
|
|
|
|
@verbatim
|
|
Ipv4BusNetwork::Ipv4BusNetwork (
|
|
Ipv4Address network,
|
|
Ipv4Mask mask,
|
|
Ipv4Address baseAddress,
|
|
DataRate bps,
|
|
Time delay,
|
|
uint32_t n)
|
|
:
|
|
Ipv4Network (network, mask, baseAddress)
|
|
{
|
|
Ipv4AddressGenerator::SeedNetwork (mask, network);
|
|
Ipv4AddressGenerator::SeedAddress (mask, baseAddress);
|
|
|
|
m_channel = CsmaTopology::CreateCsmaChannel (bps, delay);
|
|
|
|
for (uint32_t i = 0; i < n; ++i)
|
|
{
|
|
Ptr<Node> node = Create<InternetNode> ();
|
|
uint32_t nd = CsmaIpv4Topology::AddIpv4CsmaNetDevice (node, m_channel,
|
|
Mac48Address::Allocate ());
|
|
Ipv4Address address = Ipv4AddressGenerator::AllocateAddress (mask,
|
|
network);
|
|
CsmaIpv4Topology::AddIpv4Address (node, nd, address, mask);
|
|
m_nodes.push_back (node);
|
|
}
|
|
}
|
|
@end verbatim
|
|
|
|
Notice that we do the simple and straightforward thing and pass all of our
|
|
parameters to the constructor. For those unfamiliar with C++, the line after
|
|
the colon and before the opening brace (shown below),
|
|
|
|
@verbatim
|
|
:
|
|
Ipv4Network (network, mask, baseAddress)
|
|
{
|
|
@end verbatim
|
|
|
|
Passes the appropriate parameters to the constructor of the base class
|
|
@code{Ipv4Network}. There are two new calls that we haven't seen immediately
|
|
after this initialization. They are:
|
|
|
|
@verbatim
|
|
Ipv4AddressGenerator::SeedNetwork (mask, network);
|
|
Ipv4AddressGenerator::SeedAddress (mask, baseAddress);
|
|
@end verbatim
|
|
|
|
We provide an IP address generator class to allow us to programatically
|
|
allocate IP addresses. The first call to @code{SeedNetwork} gives the
|
|
address generator a starting network number to use when generating addresses.
|
|
The second call to @code{SeedAddress} gives the address generator a starting
|
|
IP address to use. There is a starting network and starting address for each
|
|
of the 32 possible network masks. Later in the for loop, you will see a
|
|
call to @code{AllocateAddress} in which the IP address for each node created
|
|
in the loop is actually generated.
|
|
|
|
The only unfamiliar call in the reset of the constructor will be:
|
|
|
|
@verbatim
|
|
m_nodes.push_back (node);
|
|
@end verbatim
|
|
|
|
This is the STL code to add the newly created node to the vector of nodes
|
|
attached to the bus.
|
|
|
|
For your convenience, we reproduce the entire bus network implementation below:
|
|
|
|
@verbatim
|
|
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
|
|
/*
|
|
* Copyright (c) 2007 University of Washington
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#include "ns3/mac48-address.h"
|
|
#include "ns3/csma-net-device.h"
|
|
#include "ns3/csma-topology.h"
|
|
#include "ns3/csma-ipv4-topology.h"
|
|
|
|
#include "ipv4-bus-network.h"
|
|
#include "ipv4-address-generator.h"
|
|
|
|
namespace ns3 {
|
|
|
|
Ipv4Network::Ipv4Network (
|
|
Ipv4Address network,
|
|
Ipv4Mask mask,
|
|
Ipv4Address address)
|
|
:
|
|
m_network (network), m_mask (mask), m_baseAddress (address)
|
|
{
|
|
}
|
|
|
|
Ipv4Network::~Ipv4Network ()
|
|
{
|
|
}
|
|
|
|
Ipv4BusNetwork::Ipv4BusNetwork (
|
|
Ipv4Address network,
|
|
Ipv4Mask mask,
|
|
Ipv4Address baseAddress,
|
|
DataRate bps,
|
|
Time delay,
|
|
uint32_t n)
|
|
:
|
|
Ipv4Network (network, mask, baseAddress)
|
|
{
|
|
Ipv4AddressGenerator::SeedNetwork (mask, network);
|
|
Ipv4AddressGenerator::SeedAddress (mask, baseAddress);
|
|
|
|
m_channel = CsmaTopology::CreateCsmaChannel (bps, delay);
|
|
|
|
for (uint32_t i = 0; i < n; ++i)
|
|
{
|
|
Ptr<Node> node = Create<InternetNode> ();
|
|
uint32_t nd = CsmaIpv4Topology::AddIpv4CsmaNetDevice (node, m_channel,
|
|
Mac48Address::Allocate ());
|
|
Ipv4Address address = Ipv4AddressGenerator::AllocateAddress (mask,
|
|
network);
|
|
CsmaIpv4Topology::AddIpv4Address (node, nd, address, mask);
|
|
m_nodes.push_back (node);
|
|
}
|
|
}
|
|
|
|
Ipv4BusNetwork::~Ipv4BusNetwork ()
|
|
{
|
|
}
|
|
|
|
Ptr<Node>
|
|
Ipv4BusNetwork::GetNode (uint32_t n)
|
|
{
|
|
return m_nodes[n];
|
|
}
|
|
|
|
}; // namespace ns3
|
|
@end verbatim
|
|
|
|
@section Using Ipv4BusNetwork
|
|
If all you ever want to do with a bus network can be captured in a topology
|
|
with four nodes on the bus, the preceeding section may seem like a colossal
|
|
waste of time. This is probably not the case, though. Now that we have a
|
|
relatively abstract bus class, we can create bus networks with 4, 40 or 4000
|
|
nodes with no additional effort.
|
|
|
|
A use of the bus network class is shown in the file
|
|
@code{bus-netowrk.cc} located in the @code{tutorial} directory. The
|
|
interesting code is,
|
|
|
|
@verbatim
|
|
Ipv4BusNetwork bus ("10.1.0.0", "255.255.0.0", "0.0.0.3",
|
|
DataRate(10000000), MilliSeconds(20), 10);
|
|
@end verbatim
|
|
|
|
Here we create a bus network with the network number ``10.1.0.0'' and the
|
|
network mask ``255.255.0.0'' that completes the IP network definition. You
|
|
can consider these together as ``10.1.0.0/16'' if you prefer. The next
|
|
parameter tells the bus to start numbering IP addresses of contained nodes at
|
|
``10.1.0.3'' (remember the network number will be combined). We provided a
|
|
data rate of 10 megabits per second and a latency of 20 milliseconds.
|
|
Finally, we ask the @code{Ipv4BusNetwork} object to create ten nodes in the
|
|
network.
|
|
|
|
If you are feeling brave, go ahead and change the number of nodes to be 100,
|
|
1000, 10,000 or more to generate larger and larger networks. Before you go
|
|
too far, remember that a trace file will be generated when you run your
|
|
resulting program and ee asked the trace facility to trace all net device
|
|
receive events. This will include the reception of the broadcast ARP request
|
|
by all of the nodes in the simulation, so this can add up quickly.
|
|
|
|
@c ========================================================================
|
|
@c Summary
|
|
@c ========================================================================
|
|
|
|
@node Summary
|
|
@chapter Summary
|
|
|
|
This concludes the first part of the tutorial. We have focused on
|
|
using the @command{ns-3} system to construct various network topologies and to
|
|
simulate sendng data across the networks; and we've shown you how to use the
|
|
trace facility to get access to simulation results.
|
|
|
|
We now encourage you to play with the system a little. Experiment with what
|
|
we have provided. Build a hierarchical network simulation. Perhaps exercise
|
|
your object design skills and create a new @code{Ipv4DumbbellNetwork} class
|
|
to create dumbbell networks using the Ipv4BusNetwork class we just created.
|
|
Hint: An Ipv4DumbbellNetwork @emph{has} two @code{Ipv4BusNetwork} objects;
|
|
a left side and a right side.
|
|
|
|
In the next part of the tutorial we are going to drop down a level and begin
|
|
examining the lower levels of the system in more detail. We are going to
|
|
explain how to change the behavior of the system and eventually how to write
|
|
new models and applications. This is a good time to make sure that you
|
|
thorougly understand what we've gone over so far.
|
|
|
|
@c ========================================================================
|
|
@c Object Model
|
|
@c ========================================================================
|
|
|
|
@node Object-Model
|
|
@chapter Object Model
|
|
|
|
There are two distinctly different meanings associated with the term Object
|
|
Model. The first speaks to the implementation of an object system --- a system
|
|
view; and the second speaks to the application programming interface (classes
|
|
or objects) one uses to access some service or system --- an application view.
|
|
|
|
As an example of the system view sense of the term, the C++ language has an
|
|
associated object model that describes how objects are laid out in memory,
|
|
how virtual functions work, how inheritance is implemented, constructor and
|
|
destructor execution ordering, template instantiation, etc.
|
|
|
|
In the case of the application view, the Document Object Model is a good
|
|
example. In the words of W3C, the Document Object Model (DOM) is an
|
|
application programming interface (API) for HTML and XML documents. It defines
|
|
the logical structure of documents and the way a document is accessed and
|
|
manipulated.
|
|
|
|
The Component Object Model (COM) from Microsoft actually spans both meanings
|
|
of the term and extends further into policy statements. From a system
|
|
perspective, COM specifies an interface definition language, the layout of
|
|
objects virtual function tables, the formats of Globally Unique Identifiers
|
|
and also specifies lifetime management mechanisms for objects via reference
|
|
counting. From the point of view of the API, COM specifies a number of
|
|
Interfaces as well as functions such as CoCreateInstance and various
|
|
threading models. The COM specification extends to policy by disallowing
|
|
implementation inheritance.
|
|
|
|
The @command{ns-3} object model takes the C++ language (system level) object
|
|
model as its basis, and extends that model by providing an API for software
|
|
componentry. You will find terms like Component, Interface and QueryInterface
|
|
in the following discussion. It is important to understand from the outset
|
|
that this is the @command{ns-3} object model, and not any other object model.
|
|
Richard Feynman (an American physicist) once described the behavior of matter
|
|
and light on a very small scale in the following way,
|
|
|
|
@quotation
|
|
``They do not behave like waves, they do not behave like particles, they do
|
|
not behave like clouds, or billiard balls, or weights on springs, or like
|
|
anything that you have ever seen.''
|
|
@end quotation
|
|
|
|
Just as students of quantum mechanics must rid themselves of preconceptions
|
|
regarding the behavior of matter at small scales, you should rid yourself of
|
|
any preconceptions you may have about components, interfaces and APIs for
|
|
software componentry before continuing. To paraphrase Feynman, @command{ns-3}
|
|
components do not behave like COM Components, or Java Beans, or CORBA
|
|
objects, or clouds or weights on springs, or like anything that you have
|
|
ever seen they are @command{ns-3} components.
|
|
|
|
@section The C++ Object Model is the Root of all Things
|
|
@command{Ns-3} is primarily a C++ system. The system is written in C++ and
|
|
one can use standard C++ mechanisms for creating and using ns-3 objects. We
|
|
do not change this at all, nor do we make any pronouncements about the
|
|
superiority of one mechanism or another. What we will do is provide
|
|
convenience functions that we think will make creating and managing simulation
|
|
objects easier.
|
|
|
|
Previously, you have seen objects created using the template function
|
|
@code{Create} as in the following example:
|
|
|
|
@verbatim
|
|
Ptr<Node> n0 = Create<InternetNode> ();
|
|
@end verbatim
|
|
|
|
This line of code, while it may be unfamiliar to some, is pure C++. If you
|
|
were to look in the header file ptr.h, you would find the following definition
|
|
of the @code{Create} template.
|
|
|
|
@verbatim
|
|
template <typename T>
|
|
Ptr<T> Create (void)
|
|
{
|
|
T *obj = new T ();
|
|
Ptr<T> p = obj;
|
|
obj->Unref ();
|
|
return p;
|
|
}
|
|
@end verbatim
|
|
|
|
As you can see, this template creates objects of type @code{T} using the
|
|
operator @code{new}. Its a little harder to find the corresponding delete ---
|
|
it's in the file @code{object.cc} inside the method @code{Object::MaybeDelete},
|
|
but when that @code{Ptr} which you see above goes out of scope it will call
|
|
@code{Unref} and ultimately the C++ @code{delete} operator will be called.
|
|
|
|
The ns-3 system uses the C++ @code{new} and @code{delete} operators, so there
|
|
is really no reason that you as a user of the ns-3 system are forbidden from
|
|
using these or any other C++ mechanism. If you so desire, you can take on
|
|
the responsibility for managing object lifetime (i.e., do not use the
|
|
@code{Ptr} smart pointer), work directly with the @code{new} and @code{delete}
|
|
operators and call methods like any C++ object as in the following example:
|
|
|
|
@verbatim
|
|
MyClass *obj = new MyClass ();
|
|
obj->Method();
|
|
delete obj;
|
|
@end verbatim
|
|
|
|
You, as a competent model author, are encouraged to use whatever methods you
|
|
think are appropriate in your private code. Remember, however, that the
|
|
public ns-3 APIs do use smart pointers to pass objects around in an effort to
|
|
reduce the burden of object lifetime management. If you do intend to export
|
|
an API publicly, you should use the same object lifetime management approaches
|
|
as those found in the ns-3 public API if only for consistency.
|
|
|
|
These APIs are there for convenience and consistency, but do not change the
|
|
fact that in ns-3 all of the objects are really just C++ objects, ultimately
|
|
created using the C++ new operator with C++ constructor semantics and are
|
|
ultimately deleted using the C++ delete operator, following C++ destructor
|
|
semantics. Although it may sometimes appear so, there is really no system-
|
|
level magic going on in ns-3. Ns-3 components and interfaces are C++ objects
|
|
just like any other object and our object model is simply a collection of APIs
|
|
built on the normal C++ object model.
|
|
|
|
@section Interfaces
|
|
There are many different ideas floating around of what exactly the term
|
|
@emph{Interface} means. Originally an interface just meant a communication
|
|
boundary between two entities. As the concepts of object oriented programming
|
|
(OOP) were surfacing in the 1980s, the term interface was applied to the
|
|
collection of access methods for the modular entities that were being defined.
|
|
|
|
Two distinct approaches developed regarding specifying access mechanisms for
|
|
objects. The OOP purists were very concerned about object reuse and were led
|
|
to Abstract Data Types (ADT). These were eventually implemented in the case
|
|
of C++, as pure virtual methods in Abstract Base Classes (ABC). Another group
|
|
of folks was more interested in simply specifying object access methods in one
|
|
place and using inheritance as the primary reuse mechanism.
|
|
|
|
Bjarne Stroustroup, the creator of C++, embraced both approaches. He makes
|
|
the following interesting observation:
|
|
|
|
@quotation
|
|
``Many classes [@dots{}] are useful both as themselves and also as bases for
|
|
derived classes. [@dots{}] Some classes, such as class @strong{Shape},
|
|
represent abstract concepts for which objects cannot exist.''
|
|
@end quotation
|
|
|
|
@command{Ns-3} does not pick and enforce a particular approach. In
|
|
@command{ns-3} an interface is determined completely by a class declaration
|
|
just as any C++ object interface is declared. If you think of an object as
|
|
an abstract concept that should be implemented by derived classes, by all
|
|
means, use the Abstract Base Class approach to interface declaration. If you
|
|
think that an object should be completely concrete and you foresee no need
|
|
to ever modify its behavior, feel free to avoid declaring any methods virtual.
|
|
If you think that an object could be useful as a base class, feel free to
|
|
declare its methods virtual. If you like to use the PIMPL idiom, again, feel
|
|
free. If you want to use any combination of these techniques, feel free.
|
|
We make no restrictions.
|
|
|
|
When we speak of an ns-3 interface, we do not worry about interface definition
|
|
languages, or pure virtual classes, or registries we just think about C++
|
|
object declarations and their associated methods. When we instantiate an
|
|
@command{ns-3} Interface, it is the C++ object model that dictates how that
|
|
object is brought into existence. When a method is called on an @command{ns-3}
|
|
Interface, it is the C++ object model that dictates how that method is
|
|
dispatched.
|
|
|
|
The only difference between a vanilla C++ object and an ns-3 Interface, is
|
|
that an object acting as an ns-3 Interface must inherit from the base class
|
|
Object. This inheritance gives the Interface object a very useful capability.
|
|
|
|
@section The Ns-3 Capital I Interface and QueryInterface
|
|
One thing that Microsoft got right in the Component Object Model was the idea
|
|
of Interface aggregation and discovery via QueryInterface. We have embraced
|
|
these ideas in @command{ns-3}. This was done primarily to address a common
|
|
problem in large software systems. A good example of this problem happens
|
|
in the @command{ns-3} Node class.
|
|
|
|
If one were to take the standard OOP view of specializing a @code{Node} into
|
|
an internet host, for example, one would typically inherit from the @code{Node}
|
|
base class and include functionality to implement such things as internet
|
|
routing and a TCP / IP protocol stack. Other types of @code{Node}s might
|
|
inherit from the node class and specialize in different ways, or further
|
|
specialize the internet host class, treating it as a base class. This can
|
|
result in a complicated inheritance tree in which some specializations are
|
|
simply not available to other branches of the tree which can make reuse
|
|
difficult or impossible. This is known as the @emph{weak base class} problem
|
|
and creates pressure to drive functionality up the inheritance tree into the
|
|
base classes. This, in turn, results in @emph{base class bloat} and the
|
|
resulting @emph{swiss army knife} base classes which end up trying to do
|
|
everything in one place.
|
|
|
|
Even if one successfully avoided these swiss army knife base classes, one
|
|
would also want to be able to treat new specializations of @code{Node}
|
|
generically in the system. This means one would pass references to the base
|
|
class (@code{Node}) across public APIs. This introduces @emph{upcasts} prior
|
|
to passing across public APIs and corresponding @emph{downcasts} on the other
|
|
side in order to gain access to required specialized functions. As the
|
|
inheritance tree becomes more complicated, this approach can cause another
|
|
related problem known as the @emph{fragile base class} problem. This happens
|
|
when changes to the base class cause unexpected problems in the various and
|
|
sundry subclasses.
|
|
|
|
These effects seem always to result in a positive feedback loop driving
|
|
everything into the base class and destroying much of the encapsulation which
|
|
is a hallmark of the object oriented approach.
|
|
|
|
@subsection Interface Composition
|
|
There is a completely different way to address the Node specialization
|
|
problem. Instead of approaching the situation using inheritance, one can
|
|
look at the problem as one of composition. We can look at the @code{Node}
|
|
class as a container of sorts that holds other objects. In this case, the
|
|
objects would be instances of the classes implementing the internetwork
|
|
routing code, or the TCP / IP protocol stack described above. This approach
|
|
preserves the encapsulation and solves the weak base class, base class bloat
|
|
and fragile base class problems; but the question of method dispatch
|
|
immediately comes to mind.
|
|
|
|
In many systems, @emph{delegation} is used. The base class, @code{Node},
|
|
in this approach would provide methods that simply forward to the objects
|
|
implementing the desired functionality. This situation clearly does not
|
|
address the base class bloat problem since dispatch methods must be added
|
|
to the base class. The situation is mitigated somewhat by pushing the
|
|
implementation of the dispatch methods to contained objects, but the
|
|
fundamental problems are still present. What is really needed is a way
|
|
to compose objects but at the same time keep the interfaces to those
|
|
objects separated.
|
|
|
|
Composition, usually called @emph{aggregation}, along with runtime Interface
|
|
discovery is the solution that Microsoft originally championed and that
|
|
@command{ns-3} has adopted. In our example a @code{Node} would contain
|
|
separate Interface objects implementing internetwork routing and TCP/IP.
|
|
These contained objects have interfaces in the C++ sense of collections of
|
|
method signatures. When objects are capable of participating in this
|
|
aggregation process, they are called @command{ns-3} Interfaces and they
|
|
receive the functionality required for this participation by inheriting
|
|
from the base class @code{Object}.
|
|
|
|
@subsection Object, interfaces and Interfaces
|
|
As mentioned above, the class that implements the aggregation mechanism for
|
|
@command{ns-3} objects is called @code{Object}. The class named @code{Object}
|
|
is simply a base class that you will inherit from if you want your objects
|
|
to support aggregation and QueryInterface. Many systems have a base class
|
|
that implements common functionality and these base classes are typically
|
|
called Object. The @command{ns-3} version of this object base class relates
|
|
primarily to Interface aggregation, although it does provide methods to help
|
|
with intrusive reference counting and tracing as well.
|
|
|
|
When a C++ object inherits from the ns-3 Object base class, it is conceptually
|
|
promoted to an ns-3 Interface (note the capital I in Interface) irrespective
|
|
of how the object was declared (e.g., as an abstract base class, concrete
|
|
class, with virtual methods, etc.). In ns-3, you should associate
|
|
inheritance from the class named @code{Object} with promotion of an object to
|
|
the status of Interface rather than the form of the Interface declaration.
|
|
|
|
When you inherit from @code{Object}, you will get new methods and an
|
|
Interface Identifier. The Interface Identifer, or @emph{iid}, is the
|
|
@command{ns-3} version of the @emph{Universally Unique ID} (UUID) or
|
|
@emph{Globally Unique ID} (GUID) found in other systems. Unlike the GUID, it
|
|
is really a dynamically created process-local ID. For now, consider it as
|
|
simply a number which the system will generate for you that uniquely
|
|
identifies an Interface class within the ns-3 system and allows you to
|
|
specify an interface type to @code{QueryInterface}.
|
|
|
|
To summarize, when you instantiate an object that inherits from the
|
|
@code{Object} class, you will have a C++ object that has four important
|
|
properties:
|
|
|
|
@itemize @bullet
|
|
@item The object has a C++ interface defined by the collection of method signatures in its inheritance tree;
|
|
@item The object has an Interface ID that uniquely identifies the C++ interface of its class;
|
|
@item The object is a container that has the ability to aggregate other interfaces;
|
|
@item The object exports a method that allows for discovery of aggregated interfaces (@code{QueryInterface}) according to Interface ID.
|
|
@end itemize
|
|
|
|
It is crucially important to understand what we have described here. A given
|
|
C++ class has an object access interface that is essentially the collection
|
|
of method signatures specified in its inheritance tree. This is a C++ object
|
|
model thing. Ns-3 provides a base class from which the class in question can
|
|
inherit and be promoted to the status of Interface. Once a class becomes
|
|
an Interface it has inherited the ability to set its own interface identifier
|
|
(@code{iid}), and exports methods to aggregate and search other Interfaces
|
|
that are added to its aggregation.
|
|
|
|
That last detail is important. In @command{ns-3} Interfaces are both
|
|
containers and specifications for object method access. We have previously
|
|
mentioned the @code{Node} class acts as a container. In fact, the @code{Node}
|
|
class inherits from @code{Object} and is itself also an @command{ns-3}
|
|
Interface. When the @code{Node} object is created it is really an aggregation
|
|
of one Interface, the @code{Node} Interface. This is generally true ---
|
|
Interfaces are both containers and Interfaces.
|
|
|
|
@subsection Aggregations
|
|
The figure below shows how an Interface could be illustrated in detail. The
|
|
line with the circle at the top of the diagram represents the appearance of the
|
|
Interface to the external world. This circle and line are called a lollipop
|
|
because of its superficial similarity to a kind of childs candy.
|
|
|
|
@sp 1
|
|
@center @image{oneif,,,,png}
|
|
|
|
You could declare this interface quite simply using a non-virtual class as
|
|
follows,
|
|
|
|
@verbatim
|
|
class A : public Object {
|
|
public:
|
|
static const InterfaceId iid;
|
|
void MethodA (void);
|
|
};
|
|
@end verbatim
|
|
|
|
The methods that are then available via the Interface labeled @code{A} in the
|
|
figure above are the methods inherited from the @code{Object} base class (
|
|
@code{QueryInterface}, @code{Ref}, and @code{Unref}) and those from class
|
|
@code{A} (@code{MethodA}). Note that you must declare an @code{InterfaceId}
|
|
for your Interface class, and it must be declared static to make it class-wide
|
|
in scope. This @code{iid} can be thought of as a kind of type information
|
|
that uniquely identifies objects as being instantiated from this class.
|
|
|
|
You can think of the arc and arrow device coming off each side of the
|
|
Interface as part of a connector. These connectors allow @code{QueryInterface}
|
|
to search aggregations for a particular @code{iid}. The figure below shows an
|
|
aggregation of three Interfaces: A, B and C. The class declarations for
|
|
classes @code{B} and @code{C} are substantially similar to that of class
|
|
@code{A}.
|
|
|
|
@sp 1
|
|
@center @image{threeif,,,,png}
|
|
|
|
You can visualize these Interfaces as being snapped together like Lego
|
|
building blocks if you like. When the Interfaces are aggregated, a
|
|
@code{QueryInterface} search path is formed through the connectors. In order
|
|
to create this aggregation we first need to create the Interface objects.
|
|
These are just normal, everyday C++ objects that we can create using the
|
|
@code{Create} template function and manage using smart pointers. The
|
|
following code should be obvious to you by now:
|
|
|
|
@verbatim
|
|
Ptr<A> a = Create<A> ();
|
|
Ptr<B> b = Create<B> ();
|
|
Ptr<C> c = Create<C> ();
|
|
@end verbatim
|
|
|
|
When you create an aggregation, you pick one of the Interfaces to act as
|
|
the container. In this case well pick Interface A. In order to aggregate
|
|
an Interface, you simply call the method @code{AddInterface} that your class
|
|
inherited from @code{Object}. The following code will aggregate Interface
|
|
@code{B} and Interface @code{C} onto the Interface (and container) @code{A}.
|
|
|
|
@verbatim
|
|
a->AddInterface (b);
|
|
a->AddInterface (c);
|
|
@end verbatim
|
|
|
|
Thats all there is to it. Now that you have those connectors snapped
|
|
together, you can ask each of the Interfaces in the aggregation for any of
|
|
the Interfaces in the aggregation. Lets look at a simple example:
|
|
|
|
@verbatim
|
|
Ptr<B> newB = a->QueryInterface<B> (B:iid);
|
|
@end verbatim
|
|
|
|
The left hand side of this assignment declares a smart pointer to the class
|
|
@code{B} to help with memory management of the returned Interface pointer.
|
|
Object lifetime management is very important when dealing with Interfaces
|
|
and our smart pointer will simply take care of it all for you.
|
|
|
|
The right hand side illustrates the basic idea of @code{QueryInterface}. We
|
|
take a take a (smart) pointer to Interface @code{A} and ask it to search the
|
|
aggregation for an interface associated with an interface identifier with
|
|
the value of @code{B:iid} which is passed as a parameter. Recall that
|
|
@code{B::iid} is the @code{static InterfaceId} of the Interface class
|
|
@code{B}. Observe that @code{QueryInterface} is a template function and the
|
|
type specified in the angle brackets, here @code{<B>}, tells it what kind of
|
|
smart pointer to return. In this case @code{QueryInterface} will find an
|
|
Interface object of type @code{B::iid} in its list of Interfaces and return a
|
|
smart pointer to @code{B} as instructed.
|
|
|
|
Now that you have those connectors snapped together, you can ask each of
|
|
the Interfaces in the aggregation for any of the Interfaces in the
|
|
aggregation. For example we could walk the Interfaces asking each for the
|
|
next in the aggregation. First we would ask the Interface pointed to by the
|
|
smart pointer a to look for the InterfaceId representing @code{B}:
|
|
|
|
@verbatim
|
|
Ptr<B> newB = a->QueryInterface<B> (B:iid);
|
|
@end verbatim
|
|
|
|
Next, we can ask the Interface pointed to by the smart pointer @code{newB}
|
|
to look for the @code{InterfaceId} representing @code{C}:
|
|
|
|
@verbatim
|
|
Ptr<C> newC = newB->QueryInterface<C> (C:iid);
|
|
@end verbatim
|
|
|
|
Then, we can ask the Interface pointed to by the smart pointer @code{newC}
|
|
to look for the InterfaceId representing A and complete our circuit of the
|
|
aggregation:
|
|
|
|
@verbatim
|
|
Ptr<A> newA = newC->QueryInterface<A> (A:iid);
|
|
@end verbatim
|
|
|
|
@code{QueryInterface} (often abbreviated QI) has some important properties
|
|
that we need to go over. Technically, QI is a @emph{symmetric},
|
|
@emph{reflexive} and @emph{transitive} operation with respect to the set of
|
|
aggregated Interfaces.
|
|
|
|
@subsubsection Symmetry
|
|
The symmetric nature of QI guarantees that if one performs a QI on a given
|
|
Interface for the Interface Id of that same interface, that
|
|
@code{QueryInterface} must succeed. The existence of interface A in the
|
|
aggregation implies the reachability of Interface A in the aggregation. This
|
|
is usually written (by Microsoft) as,
|
|
|
|
@center must succeed (A >> A)
|
|
|
|
We can illustrate this property with the code snippet,
|
|
|
|
@verbatim
|
|
Ptr<A> symmetricA = a->QueryInterface<A> (A:iid);
|
|
NS_ASSERT (symmetricA);
|
|
@end verbatim
|
|
|
|
Here we take as given an interface (smart) pointer named a on which we
|
|
perform a QI looking for the InterfaceId of that same Interface. This call
|
|
must always succeed and a smart pointer to the Interface a is returned by QI.
|
|
|
|
@subsubsection Reflexivity
|
|
Calls to QI must also be reflexive. This means that if you successfully QI
|
|
for interface B from interface A, then you must always be able to QI for A
|
|
from B. This is usually written as,
|
|
|
|
@center must succeed (A >> B, then B >> A)
|
|
|
|
This property can be illustrated with the code snippet,
|
|
|
|
@verbatim
|
|
Ptr<B> b = a->QueryInterface<B> (B:iid);
|
|
Ptr<A> reflexiveA = b->QueryInterface<A> (A:iid);
|
|
NS_ASSERT (reflexiveA);
|
|
@end verbatim
|
|
|
|
If the first @code{QueryInterface} on Interface A looking for Interface B
|
|
succeeds, then a @code{QueryInterface} on Interface B looking for Interface A
|
|
must succeed.
|
|
|
|
@subsubsection Transitivity
|
|
@code{QueryInteface} must also be transitive. This means that if one can
|
|
find Interface B from Interface A, and Interface C from Interface B, then one
|
|
must also be able to find interface C from Interface A. This is usually
|
|
written as,
|
|
|
|
@center must succeed (A >> B, and B >> C, then A >> C)
|
|
|
|
This property can be illustrated with the code snippet,
|
|
|
|
@verbatim
|
|
Ptr<B> b = a->QueryInterface<B> (B:iid);
|
|
Ptr<C> c = b->QueryInterface<C> (C:iid);
|
|
Ptr<C> transitiveC = a->QueryInterface<C> (C:iid);
|
|
NS_ASSERT (transitiveC);
|
|
@end verbatim
|
|
|
|
If you can get to Interface B from Interface A, and you can get to Interface C
|
|
from Interface B, then a QueryInterface on Interface A looking for Interface C
|
|
must also succeed.
|
|
|
|
@subsection
|
|
The final piece of this puzzle is to locate where the interface Ids actually
|
|
come from. The answer is from a static initializer that must be located in
|
|
the @code{.cc} file corresponding to the Interface. For example, to
|
|
initialize the Interface Id for the class A above, you would simply add the
|
|
following code to the source file that implements class A,
|
|
|
|
@verbatim
|
|
const InterfaceId A::iid =
|
|
MakeInterfaceId (``A'', Object::iid);
|
|
@end verbatim
|
|
|
|
This code is guaranteed by the C++ language definition to be executed before
|
|
your main procedure is entered. The call to MakeInterfaceId will assign a
|
|
process-local unique identifier to your class and associate your interface
|
|
with the name (string) ``A.'' This allows you to look up an InterfaceId by
|
|
a human readable string.
|
|
|
|
An advanced ns-3 specific feature of QueryInterface is exposed here.
|
|
@code{MakeInterfaceId} takes an @code{InterfaceId} as a parameter. This is
|
|
the @code{iid} of the base class from which you inherited. In most cases
|
|
this will be @code{Object::iid}, which is the @code{InterfaceId} of the
|
|
@code{Object} base class. In @command{ns-3}, the @code{Object} base class
|
|
has its own @code{iid} and you can QI for that @code{iid}. The @code{Object}
|
|
base class has a rough equivalence to the @emph{IUnknown} Interface in
|
|
Microsofts COM, so you can QI for @code{Object::iid} in @command{ns-3}f just
|
|
as you might QI for IID_IUnknown in COM.
|
|
|
|
The InterfaceId you pass to @code{MakeInterfaceId} is used to create an
|
|
inheritance tree in the ns-3 interface manager. This inheritance tree is also
|
|
walked in @code{QueryInterface} Interface searches. Consider a simple case
|
|
of a base class and a derived class as shown below,
|
|
|
|
@verbatim
|
|
class Base : public Object
|
|
{
|
|
public:
|
|
static const InterfaceId iid;
|
|
...
|
|
};
|
|
|
|
class Derived : public Base
|
|
{
|
|
public:
|
|
static const InterfaceId iid;
|
|
...
|
|
};
|
|
@end verbatim
|
|
|
|
To assign the InterfaceId for each of these classes, we could add two calls
|
|
to @code{MakeInterfaceId} reflecting the class hierarchy we just created.
|
|
|
|
@verbatim
|
|
const InterfaceId Base::iid =
|
|
MakeInterfaceId (``Base'', Object::iid);
|
|
|
|
const InterfaceId Derived::iid =
|
|
MakeInterfaceId (``Derived'', Base::iid);
|
|
@end verbatim
|
|
|
|
The first Interface is shown to inherit from class @code{Object} and the
|
|
second inherits from class @code{Base}. We could create these interfaces
|
|
as we usually do,
|
|
|
|
@verbatim
|
|
Ptr<Base> base = Create<Base> ();
|
|
Ptr<Derived> derived = Create<Derived> ();
|
|
@end verbatim
|
|
|
|
The derived and base @code{InterfaceIds} are either present or not present
|
|
based on the inheritance tree. For example, a QI for the @code{Base
|
|
InterfaceId} must succeed when done against a @code{Ptr<Base>}; but a QI for
|
|
the @code{Derived InterfaceId} must fail when done against a @code{Ptr<Base>}.
|
|
However, a QI for the @code{Base InterfaceId} must succeed when done against a
|
|
@code{Ptr<Derived>}; and a QI for the @code{Derived InterfaceId} must succeed
|
|
when done against a @code{Ptr<Derived>}.
|
|
|
|
This feature allows you to use implementation inheritance to easily create
|
|
new Interfaces. You are prevented from doing so in Microsoft COM, but this
|
|
was almost universally identified as a problem.
|
|
|
|
@subsection A Real Example
|
|
At this point you may be asking yourself what the point of all of this is,
|
|
since you already had those pointers laying around when you created the
|
|
objects. The typical case is that you would forget about the pointers to the
|
|
contained objects and only export a single Interface. Other Interfaces could
|
|
be discovered using QI.
|
|
|
|
Generally one tends to think of one of the Interfaces in the aggregation
|
|
as being the container and other Interfaces being aggregated to that
|
|
container. In the case of a Node, for example, it is quite natural to think
|
|
of the Node as being the container which contains Interfaces for the protocol
|
|
stacks, internet routing, etc. So, lets start developing an example by
|
|
calling the container Interface Node instead of A. The creation of this
|
|
Interface is found all over our example programs. For example, you will
|
|
find code like the following in @code{samples/simple-point-to-point.cc}:
|
|
|
|
@verbatim
|
|
Ptr<Node> n = Create<InternetNode> ();
|
|
@end verbatim
|
|
|
|
This code is described in detail in previous sections, but the important thing
|
|
to realize here is that the resulting @code{Node} is an @command{ns-3}
|
|
Interface. This is not at all obvious -- you must look at the source code to
|
|
see that this is true. Take a look at @code{src/node/node.h} and find the
|
|
class declaration for class @code{Node}. There you will find,
|
|
|
|
@verbatim
|
|
class Node : public Object
|
|
{
|
|
public:
|
|
static const InterfaceId iid;
|
|
...
|
|
};
|
|
@end verbatim
|
|
|
|
Class @code{Node} inherits from class @code{Object} and provides an
|
|
@code{InterfaceId}, therefore it is an @command{ns-3} interface. You now
|
|
know you can use @code{AddInterface} for aggregation and @code{QueryInterface}
|
|
for Interface discovery against any @code{Node} in the system.
|
|
|
|
We spoke of a protocol stack that is aggregated to a @code{Node} in our
|
|
discussions above, what we see in the real @command{ns-3} code is that this
|
|
is represented by the @code{Ipv4} Interface. If you look in
|
|
@code{src/node/ipv4.h} you will find,
|
|
|
|
@verbatim
|
|
class Ipv4 : public Object
|
|
{
|
|
public:
|
|
static const InterfaceId iid;
|
|
...
|
|
};
|
|
@end verbatim
|
|
|
|
Since class @code{Ipv4} inherits from class @code{Object} and has a
|
|
@code{static InterfaceId}, it is an @command{ns-3} Interface. If you look in
|
|
@code{src/node/ipv4.cc} you will find,
|
|
|
|
@verbatim
|
|
const InterfaceId Ipv4::iid =
|
|
MakeInterfaceId (``Ipv4'', Object::iid);
|
|
@end verbatim
|
|
|
|
After all of this reading you now know that this code snippet is asking the
|
|
system to create a unique @code{InterfaceId} for the @code{Ipv4} class and
|
|
declares that @code{Ipv4} inherits from class @code{Object}.
|
|
|
|
It turns out that the Ipv4 class is an abstract base class (ABC). There are
|
|
a number of pure virtual methods declared in that class. This means that
|
|
an @code{Ipv4} object may not be instantiated. What is instantiated is an
|
|
implementation class, called @code{Ipv4Impl}. This class inherits from
|
|
@code{Ipv4} and provides the required virtual methods. This is where
|
|
understanding what is an Interface and what is not gets tricky. The
|
|
Interface is the @code{Ipv4} class since that is where the @code{InterfaceId}
|
|
is found. The fact that you see @code{ipv4::iid} tells you that the
|
|
@code{Ipv4} class is the Interface and has the associated @code{InterfaceId}.
|
|
The class @code{Ipv4Impl} provides an implementation for the pure virtual
|
|
methods in @code{Ipv4}. Since class @code{Ipv4} cannot be instantiated, one
|
|
instantiates the @code{Ipv4Impl} class to create an @code{Ipv4} Interface.
|
|
Once the @code{Ipv4Impl} class is instantiated, the pointer to it is
|
|
immediately cast to an @code{Ipv4} pointer. Clients will then use the
|
|
@code{Ipv4} object access methods (see @code{ipv4.h}) to talk to the
|
|
@code{Ipv4Impl} object over the @code{Ipv4} Interface. I urge you to not go
|
|
any further until you thoroughly understand what youve just read.
|
|
|
|
If you now look in the file, @code{src/internet-node/internet-node.cc} you
|
|
will see the following code in @code{InternetNode::Construct} that creates the
|
|
@code{Ipv4} Interface and aggregates it to the @code{Node} interface (recall
|
|
that class @code{Node} is an Interface and class @code{InternetNode} inherits
|
|
from class @code{Node}):
|
|
|
|
@verbatim
|
|
Ptr<Ipv4Impl> ipv4Impl = Create<Ipv4Impl> (ipv4);
|
|
...
|
|
Object::AddInterface (ipv4Impl);
|
|
@end verbatim
|
|
|
|
Note that the parameter @code{ipv4} passed to the @code{Create} template
|
|
function is actually a pointer to an @code{Ipv4L3Protocol} which you can
|
|
ignore at this point --- it doesn't really have anything to do with the
|
|
@code{Ipv4} Interface.
|
|
|
|
This last example does illustrate that the fact that whether an @command{ns-3}
|
|
object is or is not an Interface can be quite well hidden. The designers of
|
|
the system had long and involved discussions on this issue and in the end
|
|
decided that mnemonic aids such as Hungarian notation were a stylistic thing
|
|
and you should just refer to the system documentation to determine what
|
|
objects are ns-3 Interfaces and what those Interfaces actually are (RTFM ---
|
|
Read the Fine Manual).
|
|
|
|
In this case, you know that the class @code{Ipv4Impl} inherits from some
|
|
Interface since there is a call to @code{AddInterface} that refers to it.
|
|
You can go to the header file @code{src/internet-node/ipv4-impl.h} and find
|
|
that @code{Ipv4Impl} inherits from class @code{Ipv4}. You then go to file
|
|
@code{src/node/ipv4.h} and see that it inherits from @code{Object} and
|
|
contains an @code{InterfaceId}. Thus the Interface added is really the
|
|
@code{Ipv4} Interface with the interface Id @code{Ipv4::iid}.
|
|
|
|
Returning to some @command{ns-3} example code, lets take a look at
|
|
@code{src/examples/simple-point-to-point.cc} again. You will find the
|
|
following code:
|
|
|
|
@verbatim
|
|
Ptr<Node> n0 = Create<InternetNode> ();
|
|
...
|
|
Ptr<Ipv4> ipv4;
|
|
ipv4 = n0->QueryInterface<Ipv4> (Ipv4::iid);
|
|
ipv4->SetDefaultRoute (Ipv4Address (``10.1.1.2''), 1);
|
|
@end verbatim
|
|
|
|
The first line creates an @code{InternetNode} object and casts the resulting
|
|
smart pointer to a @code{Node}. The next line declares a smart pointer to an
|
|
@code{Ipv4} object. Because youve been through the code with us, you know
|
|
that both the @code{Node} and the @code{Ipv4} objects are Interfaces. They
|
|
should be able to participate in a @code{QueryInterface}.
|
|
|
|
The next line confirms it. We do a @code{QueryInterface} on the @code{Node},
|
|
looking for the @code{Ipv4} Interface (@code{Ipv4::iid}).
|
|
@code{QueryInterface} then returns a smart pointer to its aggregated
|
|
@code{Ipv4} Interface. [Recall that this Interface was aggregated in
|
|
@code{InternetNode::Construct}. We knew to start looking for the aggregation
|
|
in @code{InternetNode} since we originally created an @code{InternetNode} in
|
|
the @code{Create} template function and then implicitly cast it to a
|
|
@code{Node}.]
|
|
|
|
Once you have the @code{Ipv4} smart pointer, you simply use it as if it were
|
|
any other C++ object. The last line shows this by setting the default route
|
|
for the node.
|
|
|
|
@section Caveats
|
|
There are a few things that you should remember but which may not be
|
|
immediately obvious.
|
|
|
|
@subsection Interface Ids are Associated with Classes not Objects
|
|
Interfaces are identified by an @code{InterfaceId} that is associated with
|
|
the Interface class, not the Interface object. That is indicated by the
|
|
@code{static} keyword in the declaration of the @code{iid} in the class. The
|
|
interface Id for a given Interface class exists independently of any objects
|
|
of that class that you may instantiate; and all objects of a given Interface
|
|
type share the same @code{InterfaceId}.
|
|
|
|
You cannot add more than one Interface of a given type (@code{iid}) to an
|
|
aggregation. If you need to contain a number of Interfaces of the same type
|
|
in the same aggregation, you will need to provide a separate container over
|
|
which you can iterate. For example, the @code{Node} class provides methods,
|
|
|
|
@verbatim
|
|
uint32_t GetNDevices (void) const;
|
|
Ptr<NetDevice> GetDevice (uint32_t index) const;
|
|
@end verbatim
|
|
|
|
that are used iterate over the multiple @code{NetDevice} Interfaces associated
|
|
with it.
|
|
|
|
@emph{Interface Ids do not identify objects.}
|
|
|
|
@subsection Dont use QI to Check Your Own Type.
|
|
It is tempting to use @code{QueryInterface} as a form of runtime type
|
|
information. Dont do it. You have no control over what other object may be
|
|
added to your aggregation and this may cause problems. Someone else may have
|
|
appropriated (reimplemented) your type and aggregated themselves onto your
|
|
aggregation.
|
|
|
|
Consider a socket factory implementation. Sockets can be either UDP sockets
|
|
or TCP sockets. A socket factory will have a generic @code{SocketFactory}
|
|
Interface and either a UDP specific interface for setting UDP parameters or a
|
|
similar TCP-specific interface.
|
|
|
|
Consider what might happen if you declared your socket factory as a partially
|
|
abstract base class, and then provided separate implementations for UDP and
|
|
TCP specific methods of this factory in separate concrete classes. Now
|
|
consider what might happen if you used QueryInterface in your base class
|
|
to determine if you were a UDP or a TCP factory.
|
|
|
|
If a factory, say the UDP version, were not aggregated to any other Interface,
|
|
the base class could QueryInterface on itself for the UDP-specific interface.
|
|
It could then infer that it was a UDP implementation and would then do any
|
|
UDP-specific tasks it could. [Experienced C++ folks are cringing about how
|
|
horrible this design is, but bear with me --- its a simple illustration of
|
|
a specific and perhaps not-too-obvious problem.]
|
|
|
|
If another factory, say the TCP version, were not aggregated to any other
|
|
Interface, the base class could QueryInterface on itself for the UDP-specific
|
|
interface. If this failed, it could then infer that it had a TCP
|
|
implementation and would then do any TCP-specific tasks it could.
|
|
|
|
Now, what happens when these two working objects are aggregated together.
|
|
Since the Interfaces are conceptually snapped together the TCP implementation
|
|
would suddenly begin finding the UDP Interface from the other class factory
|
|
and fail.
|
|
|
|
@emph{Interface Ids should not be used as run-time type information.}
|
|
|
|
@section Connecting the Dots
|
|
This may all sound very complicated to you if this is your first exposure to
|
|
these concepts. It may be annoying if I tell you that its really not as hard
|
|
as it sounds. Rest assured that if you take some time, look at and understand
|
|
the examples and write a little test code it will all come together for you.
|
|
Grep around the system for AddInterface and QueryInterface and take a look at
|
|
how we have used them. This will also give you a good idea of what our core
|
|
Interfaces are. If you grep for @code{::iid} you will find most, if not all
|
|
of the interface declarations in the system. The more you see this idiom in
|
|
use, the more comfortable you will be with the idea and the more you will see
|
|
how this addresses the weak base class, swiss army knife base class, and
|
|
fragile base class problems I explained at the beginning.
|
|
|
|
As I alluded to earlier, the developers had long discussions regarding how to
|
|
make navigating the QueryInterface environment easier. The primary issue was
|
|
how we could make it easier to convey to you, the model writer, that an object
|
|
was an Interface. One suggestion was to adopt the convention that classes
|
|
that implement Interfaces begin with the letter I. Microsoft does this, as
|
|
exemplified by the class IUnknown. We also toyed with the idea of beginning
|
|
our header files with i- as in i-ipv4.h. We considered forcing some structure
|
|
on Interfaces with a pure virtual class specification, the names of which
|
|
begin with an I; and corresponding implementations, the names of which begin
|
|
with a C.
|
|
|
|
In the end we decided that we were really discussing issues of programming
|
|
style, and we really could not come up with a strong reason to impose any
|
|
particular solution. In the end, we decided that we would not impose any
|
|
structure on the source code, nor impose any naming convention. We will
|
|
rely on our documentation system (Doxygen) to break out all objects with
|
|
InterfaceIds in their class hierarchy into a separate section. For now,
|
|
until this is implemented, grep is your friend.
|
|
|
|
@c ========================================================================
|
|
@c Doxygen
|
|
@c ========================================================================
|
|
|
|
@node The-Doxygen-Documentation-System
|
|
@chapter The Doxygen Documentation System
|
|
|
|
@node How-To-Change-Things
|
|
@chapter How to Change Things
|
|
|
|
@node How-To-Set-Default-Values
|
|
@chapter How to Set Default Values
|
|
|
|
@node How-To-Write-A-New-Application
|
|
@chapter How to Write a New Application
|
|
|
|
@printindex cp
|
|
|
|
@bye
|