\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:: * 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 p = Create (); @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 p = Create (); @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 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 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 (x, y); @end verbatim @cindex template!instantiate When the compiler sees @code{Add} it understands that it needs to make sure that code is instantiated (created) to perform the @code{Add} using the specified type @code{}. 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 (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 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 void MyStack::Push (T data) { ... }; @end verbatim You can then use the new templated class in the following way, @verbatim int x, y; MyStack 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{}. @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 p = ... @end verbatim To a first approximation, you can think of @code{Ptr} 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} 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} and @code{Ptr}, 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 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 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 (); @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()} 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}). 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 (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 (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 p = Create (); @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} smart pointer. For this assignment of a @code{Ptr} to a @code{Ptr} 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}. This operator takes a reference to a @code{Ptr} and not a reference to a @code{Ptr}. 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 p = Create (); @end verbatim or, @verbatim Ptr p = Create (); @end verbatim will work just fine. Of course, if you try something @emph{bad} (TM), like: @verbatim Ptr p = Create (); @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 p = Create (); @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 n0 = Create (); Ptr n1 = Create (); Ptr n2 = Create (); Ptr n3 = Create (); @end verbatim As we now understand, this will create four @code{InternetNode} objects on the heap and create four @code{Ptr} 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 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 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 n0 = Create (); Ptr n1 = Create (); Ptr n2 = Create (); Ptr n3 = Create (); Ptr 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 client = Create (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 client = Create (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 client = Create (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 client = Create (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 server = Create (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 n0 = Create (); Ptr n1 = Create (); Ptr n2 = Create (); Ptr n3 = Create (); Ptr 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 client = Create (n0, "10.1.1.2", port, maxPacketCount, interPacketInterval, packetSize); Ptr server = Create (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 n0 = Create (); Ptr n1 = Create (); Ptr n2 = Create (); Ptr n3 = Create (); Ptr 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 client = Create (n0, "10.1.1.2", port, maxPacketCount, interPacketInterval, packetSize); Ptr server = Create (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 n0 = Create (); Ptr n1 = Create (); Ptr 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 n0 = Create (); Ptr n1 = Create (); Ptr 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 client = Create (n0, "10.1.1.2", port, 1, Seconds(1.), 1024); Ptr server = Create (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 n0 = Create (); Ptr n1 = Create (); Ptr n2 = Create (); Ptr n3 = Create (); Ptr n4 = Create (); Ptr n5 = Create (); Ptr n6 = Create (); @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 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 n0 = Create (); Ptr n1 = Create (); Ptr n2 = Create (); Ptr n3 = Create (); Ptr n4 = Create (); Ptr n5 = Create (); Ptr n6 = Create (); Ptr link01 = PointToPointIpv4Topology::CreateChannel (DataRate (38400), MilliSeconds (20)); uint32_t nd01 = PointToPointIpv4Topology::AddNetDevice (n0, link01); Ptr link02 = PointToPointIpv4Topology::CreateChannel (DataRate (38400), MilliSeconds (20)); uint32_t nd02 = PointToPointIpv4Topology::AddNetDevice (n0, link02); Ptr link03 = PointToPointIpv4Topology::CreateChannel (DataRate (38400), MilliSeconds (20)); uint32_t nd03 = PointToPointIpv4Topology::AddNetDevice (n0, link03); Ptr link04 = PointToPointIpv4Topology::CreateChannel (DataRate (38400), MilliSeconds (20)); uint32_t nd04 = PointToPointIpv4Topology::AddNetDevice (n0, link04); Ptr link05 = PointToPointIpv4Topology::CreateChannel (DataRate (38400), MilliSeconds (20)); uint32_t nd05 = PointToPointIpv4Topology::AddNetDevice (n0, link05); Ptr 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 client = Create (n0, "10.1.1.2", port, 1, Seconds(1.), 1024); Ptr server = Create (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 client = Create (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 n0 = Create (); Ptr n1 = Create (); Ptr n2 = Create (); Ptr n3 = Create (); Ptr 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 n4 = Create (); Ptr n5 = Create (); Ptr n6 = Create (); Ptr n7 = Create (); Ptr 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 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 client0 = Create (n0, "10.1.2.1", port, 100, Seconds(.01), 1024); Ptr client1 = Create (n1, "10.1.2.2", port, 100, Seconds(.01), 1024); Ptr client2 = Create (n2, "10.1.2.3", port, 100, Seconds(.01), 1024); Ptr client3 = Create (n3, "10.1.2.4", port, 100, Seconds(.01), 1024); Ptr server4 = Create (n4, port); Ptr server5 = Create (n5, port); Ptr server6 = Create (n6, port); Ptr server7 = Create (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 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 > 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 GetNode (uint32_t n); private: std::vector > m_nodes; Ptr 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 = Create (); 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 = Create (); 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 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 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