@c ======================================================================== @c Begin document body here @c ======================================================================== @c ======================================================================== @c Tutorial Goals @c ======================================================================== @node Tutorial Goals @unnumbered Tutorial Goals @c This is an unnumbered section, like a preface. Numbering @c starts with section 1 (Introduction) The goal of this ns-3 tutorial is to introduce new users of 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 ns-3 system. We provide an overview of some of the important abstractions, design patterns and idioms used when writing 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 ns-3 system; @item Understand the key software conventions of ns-3; @item Modify configuration parameters of existing scripts; @item Change the simulation output (tracing, logging, statistics); @item Extend the simulator to use new objects @item Write new ns-3 applications; @item See how to port code from ns-2; @item ... (more to follow) @end itemize @c ======================================================================== @c PART: Introduction @c ======================================================================== @c The below chapters are under the major heading "Introduction" @c This is similar to the Latex \part command @c @c ======================================================================== @c Overview @c ======================================================================== @node Overview @chapter Overview @menu * For ns-2 users:: * Contributing:: * Tutorial organization:: @end menu The ns-3 simulator is a discrete-event network simulator targeted primarily for research and educational use. The @uref{http://www.nsnam.org,,ns-3 project}, started in 2006, is an open-source project. The goal of the project is to build a new network simulator primarily for research and educational use. Primary documentation for the ns-3 project is available in three forms: @itemize @bullet @item @uref{http://www.nsnam.org/doxygen/index.html,,ns-3 Doxygen/Manual}: Documentation of the public APIs of the simulator @item Tutorial (this document) @item @uref{http://www.nsnam.org/wiki/index.php,, ns-3 wiki} @end itemize The purpose of this tutorial is to introduce new 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 ns-3 documentation and provide pointers to source code for those interested in delving deeper into the workings of the system. A few key points are worth noting at the onset: @itemize @bullet @item ns-3 is not an extension of @uref{http://www.isi.edu/nsnam/ns,,ns-2}; it is a new simulator. The two simulators are both written in C++ but ns-3 is a new simulator that does not support the ns-2 APIs. Some models from ns-2 have already been ported from ns-2 to ns-3. The project will continue to maintain ns-2 while ns-3 is being built, and will study transition and integration mechanisms. @item ns-3 is open-source, and the project strives to maintain an open environment for researchers to contribute and share their software. @end itemize @node For ns-2 users @section For ns-2 users For those familiar with ns-2, the most visible outward change when moving to ns-3 is the choice of scripting language. ns-2 is typically scripted in Tcl and results of simulations can be visualized using the Network Animator @command{nam}. In ns-3 there is currently no visualization module, and Python bindings have been developed (Tcl bindings have been prototyped using @uref{http://www.swig.org,,SWIG}, but are not supported by the current development team). In this tutorial, we will concentrate on scripting directly in C++ and interpreting results via trace files. But there are similarities as well (both, for example, are based on C++ objects, and some code from ns-2 has already been ported to ns-3). We will try to highlight differences between ns-2 and ns-3 as we proceed in this tutorial. @node Contributing @section Contributing @cindex software configuration management ns-3 is a research and educational simulator, by and for the research community. It will rely on the ongoing contributions of the community to develop new models, debug or maintain existing ones, and share results. There are a few policies that we hope will encourage people to contribute to ns-3 like they have for ns-2: @itemize @bullet @item open source licensing based on GNU GPLv2 compatibility @item @uref{http://www.nsnam.org/wiki/index.php,,wiki} @item @uref{http://www.nsnam.org/wiki/index.php/Contributed_Code,,Contributed Code} page, similar to ns-2's popular @uref{http://nsnam.isi.edu/nsnam/index.php/Contributed_Code,,Contributed Code} page @item @code{src/contrib} directory (we will host your contributed code) @item open @uref{http://www.nsnam.org/bugzilla,,bug tracker} @item ns-3 developers will gladly help potential contributors to get started with the simulator (please contact @uref{http://www.nsnam.org/people.html,,one of us}) @end itemize If you are an ns user, please consider to provide your feedback, bug fixes, or code to the project. @node Tutorial organization @section Tutorial organization The tutorial assumes that new users might follow a path such as follows: @itemize @bullet @item browse the source code and documentation, to get a feel for the simulator and what it might be like to handle; @item try to download and build a copy; @item try to run a few sample programs, and perhaps change some configurations; @item look at simulation output, and try to adjust it @item study the software architecture of the system, to consider hacking it or extending it; @item write new models or port existing code to ns-3, and eventually post those models back to the community. @end itemize As a result, we have tried to organize the tutorial along the above broad sequences of events. @c ======================================================================== @c Browsing ns-3 @c ======================================================================== @node Browsing @chapter Browsing ns-3 @menu * Source code:: * Doxygen:: * Other documentation:: @end menu @node Source code @section Source code The most recent code can be browsed on our web server at the following link: @uref{http://code.nsnam.org/?sort=lastchange}. If you click on the bold repository names on the left of the page, you will see changelogs for these repositories, and links to the @emph{manifest}. From the manifest links, one can browse the source tree. The top-level directory will look something like: @verbatim AUTHORS RELEASE_NOTES examples/ src/ waf* LICENSE VERSION ns3/ tutorial/ waf.bat* README doc/ samples/ utils/ wscript @end verbatim The source code is mainly in the @code{src} directory. Example scripts are in the @code{examples} directory. Both are good directories to start browsing some code. For ns-2 users, who may be familiar with the @code{simple.tcl} example script in the ns-2 documentation, an analogous script is found in @code{examples/simple-point-to-point.cc} with a Python equivalent found in @emph{(pending Python merge)}. @node Doxygen @section Doxygen We document all of APIs using @uref{http://www.stack.nl/~dimitri/doxygen/,,Doxygen}. Current builds of this documentation are available at: @uref{http://www.nsnam.org/doxygen/index.html}, which are worth an initial look. @node Other documentation @section Other documentation See: @uref{http://www.nsnam.org/documents.html}. @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 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 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 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 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 ns-3 on the main 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 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 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 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 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 an 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 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. Cygwin provides many of the popular Linux system commands. 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, we 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 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 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 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 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 ns-3 code. @cindex template Almost any use of 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 ns-3 within the simulation core, 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 Downloading and Compiling @c ======================================================================== @node Downloading and Compiling @chapter Downloading and Compiling @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 ns-3 web site: @uref{http://www.nsnam.org/getting_started.html}. @section Downloading @cindex tarball The 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 ns-3 development team. The repositories of interest to you will be prefixed with ``ns-3''. The current development snapshot (unreleased) of 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 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. One practice is to create a directory called @code{repos} in one's home directory under which one can keep local Mercurial repositories. @emph{Hint: we will assume you do this later in the tutorial.} If you adopt that approach, you can get a copy of any of the development versions of ns-3 by typing the following into your Linux shell (assuming you have installed Mercurial): @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 ns-3 distribution. @section Building @cindex Waf!build @cindex Waf!configure @cindex Waf!debug @cindex Waf!compile We use Waf to build the 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 ns-3 programs by simply typing, @verbatim ./waf check @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 and you will see a number of software unit tests subsequently execute. @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 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 If you want to run programs under another tool such as gdb or valgrind, see this @uref{http://www.nsnam.org/wiki/index.php/User_FAQ#How_to_run_NS-3_programs_under_another_tool,,wiki entry}. @emph{Congratulations. You are now an 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 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 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 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 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 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,'' ns-3 applications run on ns-3 @code{Node}s to drive simulations in the simulated world. @cindex Application!class In 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 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 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 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 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 CreateObject @cindex Ptr In 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 = CreateObject (); @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 = CreateObject (); @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{}. @node Smart Pointers 101 @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 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 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 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 @node Object Creation @subsection Object Creation @cindex CreateObject 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 ... = CreateObject (); @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{CreateObject()} 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; ... = CreateObject (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; ... = CreateObject (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 = CreateObject (); @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 = CreateObject (); @end verbatim or, @verbatim Ptr p = CreateObject (); @end verbatim will work just fine. Of course, if you try something @emph{bad} (TM), like: @verbatim Ptr p = CreateObject (); @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 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 = CreateObject (); @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 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 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 ns-3 version of the ubiquitous hello-world program. It uses the 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 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 = CreateObject (); Ptr n1 = CreateObject (); Ptr n2 = CreateObject (); Ptr n3 = CreateObject (); @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 ns-3 class @code{Ipv4Address} which takes a dotted decimal string as a constructor parameter. We also provide a network mask using the 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 = CreateObject (); Ptr n1 = CreateObject (); Ptr n2 = CreateObject (); Ptr n3 = CreateObject (); 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 ns-3 to generate the data used to drive simulations. An @code{Application} is added to a 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 = CreateObject (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 = CreateObject (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 = CreateObject (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 = CreateObject (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 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 = CreateObject (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 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 = CreateObject (); Ptr n1 = CreateObject (); Ptr n2 = CreateObject (); Ptr n3 = CreateObject (); 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 = CreateObject (n0, "10.1.1.2", port, maxPacketCount, interPacketInterval, packetSize); Ptr server = CreateObject (n1, port); } @end verbatim @section Using the Simulation Engine @cindex model @cindex simulation executive You could say that the heart of the 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 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 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 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 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 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 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 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 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 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 = CreateObject (); Ptr n1 = CreateObject (); Ptr n2 = CreateObject (); Ptr n3 = CreateObject (); 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 = CreateObject (n0, "10.1.1.2", port, maxPacketCount, interPacketInterval, packetSize); Ptr server = CreateObject (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 tutorial-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 ns-3 distribution as @code{tutorial-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 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{tutorial-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('tutorial-csma-echo') obj.source = 'tutorial-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{tutorial-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 tutorial-csma-echo Entering directory `~/repos/ns-3-dev/build' Compilation finished successfully UDP Echo Simulation ~/repos/ns-3-dev/tutorial > @end verbatim