ns-3 Tutorial: ns-3 Project Feedback: Ns-Developers@isi - Edu
ns-3 Tutorial: ns-3 Project Feedback: Ns-Developers@isi - Edu
This is an ns-3 tutorial. Primary documentation for the ns-3 project is available in four forms: ns-3 Doxygen/Manual: Documentation of the public APIs of the simulator Tutorial (this document) Reference Manual: Reference Manual ns-3 wiki This document is written in GNU Texinfo and is to be maintained in revision control on the 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 http://www.gnu.org/licenses/.
Table of Contents
1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1 1.2 1.3 For ns-2 Users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Contributing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Tutorial Organization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.1 2.2 2.3 2.4 2.5 The Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mercurial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Waf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Development Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Socket Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 3 4 4
Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3.1 Downloading ns-3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 3.1.1 Downloading ns-3 Using Mercurial . . . . . . . . . . . . . . . . . . . . . . . 6 3.1.2 Downloading ns-3 Using a Tarball . . . . . . . . . . . . . . . . . . . . . . . . 9 3.2 Building ns-3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 3.2.1 Building with build.py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 3.2.2 Building with Waf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 3.3 Testing ns-3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 3.4 Running a Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Conceptual Overview . . . . . . . . . . . . . . . . . . . . . 14
4.1 Key Abstractions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1.1 Node . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1.2 Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1.3 Channel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1.4 Net Device . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1.5 Topology Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2 A First ns-3 Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.1 Boilerplate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.2 Module Includes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.3 Ns3 Namespace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.4 Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.5 Main Function. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.6 Topology Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.6.1 NodeContainer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.6.2 PointToPointHelper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.6.3 NetDeviceContainer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.6.4 InternetStackHelper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.6.5 Ipv4AddressHelper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.7 Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 14 14 15 15 15 16 16 17 17 18 18 19 19 19 20 21 21 21
ii 4.2.7.1 UdpEchoServerHelper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.7.2 UdpEchoClientHelper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.8 Simulator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.9 Building Your Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3 Ns-3 Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 22 23 24 25
Tweaking ns-3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
5.1 Using the Logging Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1.1 Logging Overview. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1.2 Enabling Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1.3 Adding Logging to your Code. . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2 Using Command Line Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.1 Overriding Default Attributes. . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.2 Hooking Your Own Values. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3 Using the Tracing System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.1 ASCII Tracing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.1.1 Parsing Ascii Traces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.2 PCAP Tracing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.2.1 Reading output with tcpdump . . . . . . . . . . . . . . . . . . . . . 5.3.2.2 Reading output with Wireshark . . . . . . . . . . . . . . . . . . . . 26 26 27 30 31 31 34 36 36 37 39 40 40
Building Topologies . . . . . . . . . . . . . . . . . . . . . . . 41
6.1 6.2 Building a Bus Network Topology . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Building a Wireless Network Topology . . . . . . . . . . . . . . . . . . . . . . . 50
Chapter 1: Introduction
1 Introduction
The ns-3 simulator is a discrete-event network simulator targeted primarily for research and educational use. The ns-3 project, started in 2006, is an open-source project developing ns-3. Primary documentation for the ns-3 project is available in four forms: ns-3 Doxygen/Manual: Documentation of the public APIs of the simulator Tutorial (this document) Reference Manual: Reference Manual ns-3 wiki
The purpose of this tutorial is to introduce new ns-3 users to the system in a structured way. It is sometimes dicult 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: Ns-3 is not an extension of 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. Ns-3 is open-source, and the project strives to maintain an open environment for researchers to contribute and share their software.
Chapter 1: Introduction
other hand, ns-3 does have new capabilities (such as handling multiple interfaces on nodes correctly, use of IP addressing and more alignment with Internet protocols and designs, more detailed 802.11 models, etc.). ns-2 models can usually be ported to ns-3 (a porting guide is under development). There is active development on multiple fronts for ns-3. The ns-3 developers believe (and certain early users have proven) that ns-3 is ready for active use, and should be an attractive alternative for users looking to start new simulation projects.
1.2 Contributing
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: Open source licensing based on GNU GPLv2 compatibility; wiki; Contributed Code page, similar to ns-2s popular Contributed Code page; src/contrib directory (we will host your contributed code); Open bug tracker; Ns-3 developers will gladly help potential contributors to get started with the simulator (please contact one of us). We realize that if you are reading this document, contributing back to the project is probably not your foremost concern at this point, but we want you to be aware that contributing is in the spirit of the project and that even the act of dropping us a note about your early experience with ns-3 (e.g. "this tutorial section was not clear..."), reports of stale documentation, etc. are much appreciated.
Chapter 2: Resources
2 Resources
2.1 The Web
There are several important resources of which any ns-3 user must be aware. The main web site is located at 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 http://www.nsnam.org/documents.html. You can also nd documents relating to the system architecture from this page. There is a Wiki that complements the main ns-3 web site which you will nd at http://www.nsnam.org/wiki/. You will nd 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 http://code.nsnam.org/. There you will nd the current development tree in the repository named ns-3-dev. Past releases and experimental repositories of the core developers may also be found there.
2.2 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. 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 http://www.selenic.com/mercurial/, from which you can get binary or source releases of this Software Conguration Management (SCM) system. Selenic (the developer of Mercurial) also provides a tutorial at http://www.selenic.com/mercurial/wiki/index.cgi/Tutorial/, and a QuickStart guide at http://www.selenic.com/mercurial/wiki/index.cgi/QuickStart/. You can also nd vital information about using Mercurial and ns-3 on the main ns-3 web site.
2.3 Waf
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 well known of these tools is make. Along with being the most well known, make is probably the most dicult to use in a very large and highly congurable system. Because of this, many alternatives have been developed. Recently these systems have been developed using the Python language. The build system 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 http://freehackers.org/~tnagy/waf.html.
Chapter 2: Resources
Chapter 2: Resources
be in good shape to understand the tutorial. There is a similar book on Multicast Sockets, Multicast Sockets. that covers material you may need to understand if you look at the multicast examples in the distribution.
3 Getting Started
3.1 Downloading ns-3
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 veried. We are also 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: http://www.nsnam.org/getting_started.html. The ns-3 code is available in Mercurial repositories on the server code.nsnam.org. You can also download a tarball release at http://www.nsnam.org/releases/, or you can work with repositories using Mercurial. We recommend using Mercurial unless theres a good reason not to. See the end of this section for instructions on how to get a tarball release. The simplest way to get started using Mercurial repositories is to use the ns-3-allinone environment. This is a set of scripts that manages the downloading and building of various subystems of ns-3 for you. We recommend that you begin your ns-3 adventures in this environment as it can really simplify your life at this point.
of interest to you will be prexed with ns-3. Ocial releases of ns-3 will be numbered as ns-3.<release>.<hotfix>. For example, a second hotx to a still hypothetical release nine of ns-3 would be numbered as ns-3.9.2. We have had a regression testing framework in place since the rst release. For each release, a set of output les that dene good behavior are saved. These known good output les are called reference traces and are associated with a given release by name. For example, in http://code.nsnam.org/ you will nd a repository named ns-3.1 which is the rst stable release of ns-3. You will also nd a separate repository named ns-3.1-reftraces that holds the reference traces for the ns-3.1 release. It is crucial to keep these les consistent if you want to do any regression testing of your repository. This is a good idea to do at least once to verify everything has built correctly. The current development snapshot (unreleased) of ns-3 may be found at http://code.nsnam.org/ns-3-dev/ and the associated reference traces may be found at http://code.nsnam.org/ns-3-dev-ref-traces/. The developers attempt to keep these repository in consistent, working states but they are in a development area with unreleased code present, so you may want to consider staying with an ocial release if you do not need newly- introduced features. Since the release numbers are going to be changing, I will stick with the more constant ns-3-dev here in the tutorial, but you can replace the string ns-3-dev with your choice of release (e.g., ns-3.4 and ns-3.4-ref-traces) in the text below. You can nd 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 identier. Go ahead and change into the ns-3-allinone directory you created when you cloned that repository. We are now going to use the download.py script to pull down the various pieces of ns-3 you will be using/ Go ahead and type the following into your shell (remember you can substitute the name of your chosen release number instead of ns-3-dev like "ns-3.4" and "ns-3.4-reftraces" if you want to work with a stable release). ./download.py -n ns-3-dev -r ns-3-dev-ref-traces As the hg (Mercurial) command executes, you should see something like the following, # # Get NS-3 # Cloning ns-3 branch => hg clone http://code.nsnam.org/ns-3-dev ns-3-dev requesting all changes adding changesets adding manifests adding file changes added 4292 changesets with 15368 changes to 1671 files 823 files updated, 0 files merged, 0 files removed, 0 files unresolved This is output by the download script as it fetches the actual ns-3 code from the repository. Next, you should see something like,
# # Get the regression traces # Synchronizing reference traces using Mercurial. => hg clone http://code.nsnam.org/ns-3-dev-ref-traces ns-3-dev-ref-traces requesting all changes adding changesets adding manifests adding file changes added 79 changesets with 1102 changes to 222 files 206 files updated, 0 files merged, 0 files removed, 0 files unresolved This is the download script fetching the reference trace les for you. The download script is smart enough to know that on some platforms various pieces of ns-3 are not supported. On your platform you may not see some of these pieces come down. However, on most platforms, the process should continue with something like, # # Get PyBindGen # Required pybindgen version: 0.10.0.630 Trying to fetch pybindgen; this will fail if no network connection is available. => bzr checkout -rrevno:630 https://launchpad.net/pybindgen pybindgen Fetch was successful. This was the download script getting the Python bindings generator for you. Next you should see (modulo platform variations) something along the lines of, # # Get NSC # Required NSC version: nsc-0.5.0 Retrieving nsc from https://secure.wand.net.nz/mercurial/nsc => hg clone https://secure.wand.net.nz/mercurial/nsc nsc requesting all changes adding changesets adding manifests adding file changes added 270 changesets with 17375 changes to 14991 files 10614 files updated, 0 files merged, 0 files removed, 0 files unresolved This part of the process is the script downloading the Network Simulation Cradle for you. After the clone command completes, you should have several new directories under ~/repos/ns-3-allinone: build.py* constants.py constants.pyc dist.py* download.py* ns-3-dev/ ns-3-dev-ref-traces/ nsc/ pybindgen/ README util.py util.pyc
Hit Ctrl
Go ahead and change into ns-3-dev under your ~/repos/ns-3-allinone directory. You should see something like the following there: AUTHORS bindings/ CHANGES.html doc/ examples/ LICENSE ns3/ README regression/ regression.py RELEASE_NOTES samples/ scratch/ src/ utils/ VERSION waf* waf.bat* wscript wutils.py
10
Once the project has built you can say goodbye to your old friends, the ns-3-allinone scripts. You got what you needed from them and will now interact directly with Waf and we do it in the ns-3-dev directory and not in the ns-3-allinone directory. Go ahead and change into the ns-3-dev directory (or the directory for the appropriate release you downloaded. cd ns-3-dev
11
Checking for NSC supported architecture x86_64 : ok Package goocanvas was not found in the pkg-config search path. Perhaps you should add the directory containing goocanvas.pc to the PKG_CONFIG_PATH environment variable No package goocanvas found Checking for pkg-config flags for MOBILITY_VISUALIZER : not found Checking for program python : ok /usr/bin/python Checking for Python version >= 2.3 : ok 2.5.2 Checking for library python2.5 : ok Checking for program python2.5-config : ok /usr/bin/python2.5-config Checking for header Python.h : ok Checking for -fvisibility=hidden support : yes Checking for pybindgen location : ok ../pybindgen (guessed) Checking for Python module pybindgen : ok Checking for pybindgen version : ok 0.10.0.630 Checking for Python module pygccxml : ok Checking for pygccxml version : ok 0.9.5 Checking for program gccxml : ok /usr/local/bin/gccxml Checking for gccxml version : ok 0.9.0 Checking for program sudo : ok /usr/bin/sudo Checking for program hg : ok /usr/bin/hg Checking for program valgrind : ok /usr/bin/valgrind ---- Summary of optional NS-3 features: Threading Primitives : enabled Real Time Simulator : enabled Emulated Net Device : enabled Tap Bridge : enabled GtkConfigStore : enabled XmlIo : not enabled (library libxml-2.0 >= 2.7 not found) SQlite stats data output : enabled Network Simulation Cradle : enabled Python Bindings : enabled Python API Scanning Support : enabled Use sudo to set suid bit : not enabled (option --enable-sudo not selected) Configuration finished successfully (00:00:02); project is now ready to build. Note the last part of the above output. Some ns-3 options are not enabled by default or require support from the underlying system to work properly For instance, to enable XmlTo, the library libxml-2.0 must be found on the system. in the example above, this library was not found and the corresponding feature was not enabled. There is a feature to use sudo to set the suid bit of certain programs. This was not enabled by default. Now go ahead and switch back to the debug build. ./waf -d debug configure The build system is now congured and you can build the debug versions of the ns-3 programs by simply typing, ./waf
12
Some waf commands are meaningful during the build phase and some commands are valid in the conguration phase. For example, if you wanted to use the emulation features of ns-3 you might want to enable setting the suid bit using sudo. This is a conguration command, and so you could have run the following command ./waf -d debug --enable-sudo configure If you had done this, waf would have run sudo to change the socket creator programs to run as root. There are many other congure- and build-time options available in waf. To explore these options, type: ./waf -- help Well use some of the testing-related commands in the next section. Okay, sorry, I made you build the ns-3 part of the system twice, but now you know how to change the conguration and build optimized code.
13
During regression testing Waf will run a number of tests that generate what we call trace les. The content of these trace les are compared with the reference traces. If they are identical, the regression tests report a PASS status. If a regression test fails you will see a FAIL indication along with a pointer to the oending trace le and its associated reference trace le along with a suggestion on di parameters and options in order to see what has gone awry. If the error was discovered in a pcap le, it will be useful to convert the pcap les to text using tcpdump prior to comparison. Some regression tests wmay be SKIPped if the required support is not present. To run the regression tests, you provide Waf with the regression ag. ./waf --regression You should see messages indicating that many tests are being run and are passing. Entering directory repos/ns-3-allinone/ns-3-dev/build [647/669] regression-test (test-csma-bridge) [648/669] regression-test (test-csma-broadcast) [649/669] regression-test (test-csma-multicast) [650/669] regression-test (test-csma-one-subnet) PASS test-csma-multicast [651/669] regression-test (test-csma-packet-socket) PASS test-csma-bridge ... Regression testing summary: PASS: 22 of 22 tests passed Build finished successfully (00:00:23) If you want to take a look at an example of what might be checked during a regression test, you can do the following: cd build/debug/regression/traces/second.ref tcpdump -nn -tt -r second-2-0.pcap The output should be clear to anyone who is familiar with tcpdump or net sniers. Well have much more to say on pcap les later in this tutorial. Remember to cd back into the top-level ns-3 directory after you are done: cd ../../../../..
14
4 Conceptual Overview
The rst thing we need to do before actually starting to look at or write ns-3 code is to explain a few core concepts and abstractions in the system. Much of this may appear transparently obvious to some, but we recommend taking the time to read through this section just to ensure you are starting on a rm foundation.
4.1.1 Node
In Internet jargon, a computing device that connects to a network is called a host or sometimes an end system. Because ns-3 is a network simulator, not specically an 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 node. In ns-3 the basic computing device abstraction is called the node. This abstraction is represented in C++ by the class Node. The Node class provides methods for managing the representations of computing devices in simulations. You should think of a 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.
4.1.2 Application
Typically, computer software is divided into two broad classes. 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 benet a user. A user would typically run an application that acquires and uses the resources controlled by the system software to accomplish some goal. 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 Nodes to drive simulations in the simulated world. 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 Application. The Application class provides methods for managing the representations of our version of user-level applications in simulations. Developers are expected to specialize the Application class in the object-oriented programming sense to create new applications. In this tutorial, we will use specializations of class Application called UdpEchoClientApplication and UdpEchoServerApplication. As you might expect, these applications compose a client/server application set used to generate and echo simulated network packets
15
4.1.3 Channel
In the real world, one can connect a computer to a network. Often the media over which data ows in these networks are called 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 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 Channel. The Channel class provides methods for managing communication subnetwork objects and connecting nodes to them. Channels may also be specialized by developers in the object oriented programming sense. A Channel specialization may model something as simple as a wire. The specialized Channel can also model things as complicated as a large Ethernet switch, or three-dimensional space full of obstructions in the case of wireless networks. We will use specialized versions of the Channel called CsmaChannel, PointToPointChannel and WifiChannel in this tutorial. The CsmaChannel, for example, models a version of a communication subnetwork that implements a carrier sense multiple access communication medium. This gives us Ethernet-like functionality.
16
4.2.1 Boilerplate
The rst line in the le is an emacs mode line. This tells emacs about the formatting conventions (coding style) we use in our source code. /* -*- Mode:C++; c-file-style:gnu; indent-tabs-mode:nil; -*- */ This is always a somewhat controversial subject, so we might as well get it out of the way immediately. The ns-3 project, like most large projects, has adopted a coding style to which all contributed code must adhere. If you want to contribute your code to the project, you will eventually have to conform to the ns-3 coding standard as described in the le doc/codingstd.txt or shown on the project web page here. We recommend that you, well, just get used to the look and feel of ns-3 code and adopt this standard whenever you are working with our code. All of the development team and contributors have done so with various amounts of grumbling. The emacs mode line above makes it easier to get the formatting correct if you use the emacs editor. The ns-3 simulator is licensed using the GNU General Public License. You will see the appropriate GNU legalese at the head of every le in the ns-3 distribution. Often you will see a copyright notice for one of the institutions involved in the ns-3 project above the GPL text and an author listed below. /* * This program is free software; you can redistribute it and/or modify
17
* 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 */
To help our high-level script users deal with the large number of include les present in the system, we group includes according to relatively large modules. We provide a single include le that will recursively load all of the include les used in each module. Rather than having to look up exactly what header you need, and possibly have to get a number of dependencies right, we give you the ability to load a group of les at a large granularity. This is not the most ecient approach but it certainly makes writing scripts much easier. Each of the ns-3 include les is placed in a directory called ns3 (under the build directory) during the build process to help avoid include le name collisions. The ns3/coremodule.h le corresponds to the ns-3 module you will nd in the directory src/core in your downloaded release distribution. If you list this directory you will nd a large number of header les. When you do a build, Waf will place public header les in an ns3 directory under the appropriate build/debug or build/optimized directory depending on your conguration. Waf will also automatically generate a module include le to load all of the public header les. Since you are, of course, following this tutorial religiously, you will already have done a ./waf -d debug configure in order to congure the project to perform debug builds. You will also have done a ./waf to build the project. So now if you look in the directory build/debug/ns-3 you will nd the four module include les shown above. You can take a look at the contents of these les and nd that they do include all of the public include les in their respective modules.
18
The ns-3 project is implemented in a C++ namespace called ns3. This groups all ns-3related declarations in a scope outside the global namespace, which we hope will help with integration with other code. The C++ using statement introduces the ns-3 namespace into the current (global) declarative region. This is a fancy way of saying that after this declaration, you will not have to type ns3:: scope resolution operator before all of the ns-3 code in order to use it. If you are unfamiliar with namespaces, please consult almost any C++ tutorial and compare the ns3 namespace and usage here with instances of the std namespace and the using namespace std; statements you will often nd in discussions of cout and streams.
4.2.4 Logging
The next line of the script is the following, NS_LOG_COMPONENT_DEFINE ("FirstScriptExample"); We will use this statement as a convenient place to talk about our Doxygen documentation system. If you look at the project web site, ns-3 project, you will nd a link to APIs (Doxygen) in the navigation bar. If you select this link, you will be taken to our documentation page. Along the left side, you will nd a graphical representation of the structure of the documentation. A good place to start is the NS-3 Modules book. If you expand Modules you will see a list of ns-3 module documentation. The concept of module here ties directly into the module include les discussed above. It turns out that the ns-3 logging subsystem is part of the core module, so go ahead and expand that documentation node. Now, expand the Debugging book and then select the Logging page. You should now be looking at the Doxygen documentation for the Logging module. In the list of #defines at the top of the page you will see the entry for NS_LOG_COMPONENT_ DEFINE. Before jumping in, it would probably be good to look for the Detailed Description of the logging module to get a feel for the overall operation. You can either scroll down or select the More... link under the collaboration diagram to do this. Once you have a general idea of what is going on, go ahead and take a look at the specic NS_LOG_COMPONENT_DEFINE documentation. I wont duplicate the documentation here, but to summarize, this line declares a logging component called FirstScriptExample that allows you to enable and disable console message logging by reference to the name.
19
If you have read over the Logging component documentation you will have seen that there are a number of levels of logging verbosity/detail that you can enable on each component. These two lines of code enable debug logging at the INFO level for echo clients and servers. This will result in the application printing out messages as packets are sent and received during the simulation. Now we will get directly to the business of creating a topology and running a simulation. We use the topology helper objects to make this job as easy as possible.
4.2.6.2 PointToPointHelper
We are constructing a point to point link, and, in a pattern which will become quite familiar to you, we use a topology helper object to do the low-level work required to put the link together. Recall that two of our key abstractions are the NetDevice and the Channel. In the real world, these terms correspond roughly to peripheral cards and network cables. Typically these two things are intimately tied together and one cannot expect to interchange, for example, Ethernet devices and wireless channels. Our Topology Helpers follow this intimate coupling and therefore you will use a single PointToPointHelper to congure and connect ns-3 PointToPointNetDevice and PointToPointChannel objects in this script. The next three lines in the script are,
20
PointToPointHelper pointToPoint; pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps")); pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms")); The rst line, PointToPointHelper pointToPoint; instantiates a PointToPointHelper object on the stack. From a high-level perspective the next line, pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps")); tells the PointToPointHelper object to use the value 5mbps (ve megabits per second) as the DataRate when it creates a PointToPointNetDevice object. From a more detailed perspective, the string DataRate corresponds to what we call an Attribute of the PointToPointNetDevice. If you look at the Doxygen for class ns3::PointToPointNetDevice and nd the documentation for the GetTypeId method, you will nd a list of Attributes dened for the device. Among these is the DataRate Attribute. Most user-visible ns-3 objects have similar lists of Attributes. We use this mechanism to easily congure simulations without recompiling as you will see in a following section. Similar to the DataRate on the PointToPointNetDevice you will nd a Delay Attribute associated with the PointToPointChannel. The nal line, pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms")); tells the PointToPointHelper to use the value 2ms (two milliseconds) as the value of the transmission delay of every point to point channel it subsequently creates.
4.2.6.3 NetDeviceContainer
At this point in the script, we have a NodeContainer that contains two nodes. We have a PointToPointHelper that is primed and ready to make PointToPointNetDevices and wire PointToPointChannel objects between them. Just as we used the NodeContainer topology helper object to create the Nodes for our simulation, we will ask the PointToPointHelper to do the work involved in creating, conguring and installing our devices for us. We will need to have a list of all of the NetDevice objects that are created, so we use a NetDeviceContainer to hold them just as we used a NodeContainer to hold the nodes we created. The following two lines of code, NetDeviceContainer devices; devices = pointToPoint.Install (nodes); will nish conguring the devices and channel. The rst line declares the device container mentioned above and the second does the heavy lifting. The Install method of the PointToPointHelper takes a NodeContainer as a parameter. Internally, a NetDeviceContainer is created. For each node in the NodeContainer (there must be exactly two for a point-to-point link) a PointToPointNetDevice is created and saved in the device container. A PointToPointChannel is created and the two PointToPointNetDevices are attached. When objects are created by the PointToPointHelper, the Attributes previously set in the helper are used to initialize the corresponding Attributes in the created objects.
21
After executing the pointToPoint.Install (nodes) call we will have two nodes, each with an installed point-to-point net device and a point-to-point channel between them. Both devices will be congured to transmit data at ve megabits per second over the channel which has a two millisecond transmission delay.
4.2.6.4 InternetStackHelper
We now have nodes and devices congured, but we dont have any protocol stacks installed on our nodes. The next two lines of code will take care of that. InternetStackHelper stack; stack.Install (nodes); The InternetStackHelper is a topology helper that is to internet stacks what the PointToPointHelper is to point-to-point net devices. The Install method takes a NodeContainer as a parameter. When it is executed, it will install an Internet Stack (TCP, UDP, IP, etc.) on each of the nodes in the node container.
4.2.6.5 Ipv4AddressHelper
Next we need to associate the devices on our nodes with IP addresses. We provide a topology helper to manage the allocation of IP addresses. The only user-visible API is to set the base IP address and network mask to use when performing the actual address allocation (which is done at a lower level inside the helper). The next two lines of code in our example script, first.cc, Ipv4AddressHelper address; address.SetBase ("10.1.1.0", "255.255.255.0"); declare an address helper object and tell it that it should begin allocating IP addresses from the network 10.1.1.0 using the mask 255.255.255.0 to dene the allocatable bits. By default the addresses allocated will start at one and increase monotonically, so the rst address allocated from this base will be 10.1.1.1, followed by 10.1.1.2, etc. The low level ns-3 system actually remembers all of the IP addresses allocated and will generate a fatal error if you accidentally cause the same address to be generated twice (which is a very hard to debug error, by the way). The next line of code, Ipv4InterfaceContainer interfaces = address.Assign (devices); performs the actual address assignment. In ns-3 we make the association between an IP address and a device using an Ipv4Interface object. Just as we sometimes need a list of net devices created by a helper for future reference we sometimes need a list of Ipv4Interface objects. The Ipv4InterfaceContainer provides this functionality. Now we have a point-to-point network built, with stacks installed and IP addresses assigned. What we need at this point are applications to generate trac.
4.2.7 Applications
Another one of the core abstractions of the ns-3 system is the Application. In this script we use two specializations of the core ns-3 class Application called UdpEchoServerApplication and UdpEchoClientApplication. Just as we have in our previous explanations, we use helper objects to help congure and manage the underlying
22
objects. Here, we use UdpEchoServerHelper and UdpEchoClientHelper objects to make our lives easier.
4.2.7.1 UdpEchoServerHelper
The following lines of code in our example script, first.cc, are used to set up a UDP echo server application on one of the nodes we have previously created. UdpEchoServerHelper echoServer (9); ApplicationContainer serverApps = echoServer.Install (nodes.Get (1)); serverApps.Start (Seconds (1.0)); serverApps.Stop (Seconds (10.0)); The rst line of code in the above snippet declares the UdpEchoServerHelper. As usual, this isnt the application itself, it is an object used to help us create the actual applications. One of our conventions is to place required Attributes in the helper constructor. In this case, the helper cant do anything useful unless it is provided with a port number that the client also knows about. Rather than just picking one and hoping it all works out, we require the port number as a parameter to the constructor. The constructor, in turn, simply does a SetAttribute with the passed value. You can, if desired, set the Port Attribute to another value later. Similar to many other helper objects, the UdpEchoServerHelper object has an Install method. It is the execution of this method that actually causes the underlying echo server application to be instantiated and attached to a node. Interestingly, the Install method takes a NodeContainter as a parameter just as the other Install methods we have seen. This is actually what is passed to the method even though it doesnt look so in this case. There is a C++ implicit conversion at work here. We now see that echoServer.Install is going to install a UdpEchoServerApplication on the node found at index number one of the NodeContainer we used to manage our nodes. Install will return a container that holds pointers to all of the applications (one in this case since we passed a NodeContainer containing one node) created by the helper. Applications require a time to start generating trac and may take an optional time to stop. We provide both. These times are set using the ApplicationContainer methods Start and Stop. These methods take Time parameters. In this case, we use an explicit C++ conversion sequence to take the C++ double 1.0 and convert it to an ns-3 Time object using a Seconds cast. The two lines, serverApps.Start (Seconds (1.0)); serverApps.Stop (Seconds (10.0)); will cause the echo server application to Start (enable itself) at one second into the simulation and to Stop (disable itself) at ten seconds into the simulation. By virtue of the fact that we have implicilty declared a simulation event (the application stop event) to be executed at ten seconds, the simulation will last at least ten seconds.
4.2.7.2 UdpEchoClientHelper
The echo client application is set up in a method substantially similar to that for the server. There is an underlying UdpEchoClientApplication that is managed by an UdpEchoClientHelper.
23
UdpEchoClientHelper echoClient (interfaces.GetAddress (1), 9); echoClient.SetAttribute ("MaxPackets", UintegerValue (1)); echoClient.SetAttribute ("Interval", TimeValue (Seconds (1.))); echoClient.SetAttribute ("PacketSize", UintegerValue (1024)); ApplicationContainer clientApps = echoClient.Install (nodes.Get (0)); clientApps.Start (Seconds (2.0)); clientApps.Stop (Seconds (10.0)); For the echo client, however, we need to set ve dierent Attributes. The rst two Attributes are set during construction of the UdpEchoClientHelper. We pass parameters that are used (internally to the helper) to set the RemoteAddress and RemotePort Attributes in accordance with our convention to make required Attributes parameters in the helper constructors. Recall that we used an Ipv4InterfaceContainer to keep track of the IP addresses we assigned to our devices. The zeroth interface in the interfaces container is going to correspond to the IP address of the zeroth node in the nodes container. The rst interface in the interfaces container corresponds to the IP address of the rst node in the nodes container. So, in the rst line of code (from above), we are creating the helper and telling it so set the remote address of the client to be the IP address assigned to the node on which the server resides. We also tell it to arrange to send packets to port nine. The MaxPackets Attribute tells the client the maximum number of packets we allow it to send during the simulation. The Interval Attribute tells the client how long to wait between packets, and the PacketSize Attribute tells the client how large its packet payloads should be. With this particular combination of Attributes, we are telling the client to send one 1024-byte packet. Just as in the case of the echo server, we tell the echo client to Start and Stop, but here we start the client one second after the server is enabled (at two seconds into the simulation).
4.2.8 Simulator
What we need to do at this point is to actually run the simulation. This is done using the global function Simulator::Run. Simulator::Run (); When we previously called the methods, serverApps.Start (Seconds (1.0)); serverApps.Stop (Seconds (10.0)); ... clientApps.Start (Seconds (2.0)); clientApps.Stop (Seconds (10.0)); we actually scheduled events in the simulator at 1.0 seconds, 2.0 seconds and 10.0 seconds. When Simulator::Run is called, the system will begin looking through the list of scheduled events and executing them. First it will run the event at 1.0 seconds, which will enable the echo server application. Then it will run the event scheduled for t=2.0 seconds which will start the echo client application. The start event implementation in the echo client
24
application will begin the data transfer phase of the simulation by sending a packet to the server. The act of sending the packet to the server will trigger a chain of events that will be automatically scheduled behind the scenes and which will perform the mechanics of the packet echo according to the various timing parameters that we have set in the script. Eventually, since we only send one packet, the chain of events triggered by that single client echo request will taper o and the simulation will go idle. Once this happens, the remaining events will be the Stop events for the server and the client. When these events are executed, there are no further events to process and Simulator::Run returns. The simulation is complete. All that remains is to clean up. This is done by calling the global function Simulator::Destroy. As the helper functions (or low level ns-3 code) executed, they arranged it so that hooks were inserted in the simulator to destroy all of the objects that were created. You did not have to keep track of any of these objects yourself all you had to do was to call Simulator::Destroy and exit. The ns-3 system took care of the hard part for you. The remaining lines of our rst ns-3 script, first.cc, do just that: Simulator::Destroy (); return 0; }
25
echo server say that it has received the 1024 bytes from 10.1.1.1. The echo server silently echoes the packet and you see the echo client log that it has received its packet back from the server.
26
5 Tweaking ns-3
5.1 Using the Logging Module
We have already taken a brief look at the ns-3 logging module while going over the first.cc script. We will now take a closer look and see what kind of use-cases the logging subsystem was designed to cover.
27
28
UdpEchoClientApplication:DoDispose() UdpEchoClientApplication:~UdpEchoClient() The additional debug information provided by the application is from the NS LOG FUNCTION level. This shows every time a function in the application is called during script execution. Note that there are no requirements in the ns-3 system that models must support any particular logging functionality. The decision regarding how much information is logged is left to the individual model developer. In the case of the echo applications, a good deal of log output is available. You can now see a log of the function calls that were made to the application. If you look closely you will notice a single colon between the string UdpEchoClientApplication and the method name where you might have expected a C++ scope operator (::). This is intentional. The name is not actually a class name, it is a logging component name. When there is a one-to-one correspondence between a source le and a class, this will generally be the class name but you should understand that it is not actually a class name, and there is a single colon there instead of a double colon to remind you in a relatively subtle way to conceptually separate the logging component name from the class name. It turns out that in some cases, it can be hard to determine which method actually generates a log message. If you look in the text above, you may wonder where the string Received 1024 bytes from 10.1.1.2 comes from. You can resolve this by ORing the prefix_func level into the NS_LOG environment variable. Try doing the following, export NS_LOG=UdpEchoClientApplication=level_all|prefix_func Note that the quotes are required since the vertical bar we use to indicate an OR operation is also a Unix pipe connector. Now, if you run the script you will see that the logging system makes sure that every message from the given log component is prexed with the component name. Entering directory repos/ns-3-allinone/ns-3-dev/build Build finished successfully (00:00:00) UdpEchoClientApplication:UdpEchoClient() UdpEchoClientApplication:StartApplication() UdpEchoClientApplication:ScheduleTransmit() UdpEchoClientApplication:Send() UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2 Received 1024 bytes from 10.1.1.1 UdpEchoClientApplication:HandleRead(0x638180, 0x6389b0) UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2 UdpEchoClientApplication:StopApplication() UdpEchoClientApplication:DoDispose() UdpEchoClientApplication:~UdpEchoClient() You can now see all of the messages coming from the UDP echo client application are identied as such. The message Received 1024 bytes from 10.1.1.2 is now clearly identied as coming from the echo client application. The remaining message must be coming from the UDP echo server application. We can enable that component by entering a colon separated list of components in the NS LOG environment variable.
29
export NS_LOG=UdpEchoClientApplication=level_all|prefix_func: UdpEchoServerApplication=level_all|prefix_func Warning: You will need to remove the newline after the : in the example text above which is only there for document formatting purposes. Now, if you run the script you will see all of the log messages from both the echo client and server applications. You may see that this can be very useful in debugging problems. Entering directory repos/ns-3-allinone/ns-3-dev/build Build finished successfully (00:00:00) UdpEchoServerApplication:UdpEchoServer() UdpEchoClientApplication:UdpEchoClient() UdpEchoServerApplication:StartApplication() UdpEchoClientApplication:StartApplication() UdpEchoClientApplication:ScheduleTransmit() UdpEchoClientApplication:Send() UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2 UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1 UdpEchoServerApplication:HandleRead(): Echoing packet UdpEchoClientApplication:HandleRead(0x638320, 0x638b50) UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2 UdpEchoServerApplication:StopApplication() UdpEchoClientApplication:StopApplication() UdpEchoClientApplication:DoDispose() UdpEchoServerApplication:DoDispose() UdpEchoClientApplication:~UdpEchoClient() UdpEchoServerApplication:~UdpEchoServer() It is also sometimes useful to be able to see the simulation time at which a log message is generated. You can do this by ORing in the prex time bit. export NS_LOG=UdpEchoClientApplication=level_all|prefix_func|prefix_time: UdpEchoServerApplication=level_all|prefix_func|prefix_time Again, you will have to remove the newline above. If you run the script now, you should see the following output: Entering directory repos/ns-3-allinone/ns-3-dev/build Build finished successfully (00:00:00) 0s UdpEchoServerApplication:UdpEchoServer() 0s UdpEchoClientApplication:UdpEchoClient() 1s UdpEchoServerApplication:StartApplication() 2s UdpEchoClientApplication:StartApplication() 2s UdpEchoClientApplication:ScheduleTransmit() 2s UdpEchoClientApplication:Send() 2s UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2 2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1 2.00369s UdpEchoServerApplication:HandleRead(): Echoing packet 2.00737s UdpEchoClientApplication:HandleRead(0x638490, 0x638cc0) 2.00737s UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2 10s UdpEchoServerApplication:StopApplication()
30
10s UdpEchoClientApplication:StopApplication() UdpEchoClientApplication:DoDispose() UdpEchoServerApplication:DoDispose() UdpEchoClientApplication:~UdpEchoClient() UdpEchoServerApplication:~UdpEchoServer() You can see that the constructor for the UdpEchoServer was called at a simulation time of 0 seconds. This is actually happening before the simulation starts. The same for the UdpEchoClient constructor. Recall that the scratch/first.cc script started the echo server application at one second into the simulation. You can now see that the StartApplication method of the server is, in fact, called at one second (or one billion nanoseconds). You can also see that the echo client application is started at a simulation time of two seconds as we requested in the script. You can now follow the progress of the simulation from the ScheduleTransmit call in the client that calls Send to the HandleRead callback in the echo server application. Note that the elapsed time as the packet is sent across the point-to-point link is 3.6864 milliseconds. You see the echo server logging a message telling you that it has echoed the packet and then, after a delay, you see the echo client receive the echoed packet in its HandleRead method. There is a lot that is happening under the covers in this simulation that you are not seeing as well. You can very easily follow the entire process by turning on all of the logging components in the system. Try setting the NS_LOG variable to the following, export NS_LOG=*=level_all|prefix_func|prefix_time The asterisk above is the logging component wildcard. This will turn on all of the logging in all of the components used in the simulation. I wont reproduce the output here (as of this writing it produces 974 lines of output for the single packet echo) but you can redirect this information into a le and look through it with your favorite editor if you like, ./waf --run scratch/myfirst > log.out 2>&1 I personally use this volume of logging quite a bit when I am presented with a problem and I have no idea where things are going wrong. I can follow the progress of the code quite easily without having to set breakpoints and step through code in a debugger. When I have a general idea about what is going wrong, I transition into a debugger for ne-grained examination of the problem. This kind of output can be especially useful when your script does something completely unexpected. If you are stepping using a debugger you may miss an unexpected excursion completely. Logging the excursion makes it quickly visible.
31
Open scratch/myfirst.cc in your favorite editor and add the line, NS_LOG_INFO ("Creating Topology"); right before the lines, NodeContainer nodes; nodes.Create (2); Now build the script using waf and clear the NS_LOG variable to turn o the torrent of logging we previously enabled: ./waf export NS_LOG= Now, if you run the script, ./waf --run scratch/myfirst you will not see your new message since its associated logging component (FirstScriptExample) has not been enabled. In order to see your message you will have to enable the FirstScriptExample logging component with a level greater than or equal to NS_LOG_INFO. If you just want to see this particular level of logging, you can enable it by, export NS_LOG=FirstScriptExample=info If you now run the script you will see your new Creating Topology log message, Entering directory repos/ns-3-allinone/ns-3-dev/build Build finished successfully (00:00:00) Creating Topology Sent 1024 bytes to 10.1.1.2 Received 1024 bytes from 10.1.1.1 Received 1024 bytes from 10.1.1.2
32
This simple two line snippet is actually very useful by itself. It opens the door to the ns-3 global variable and Attribute systems. Go ahead and add that two lines of code to the scratch/first.cc script at the start of main. Go ahead and build the script and run it, but ask the script for help in the following way, ./waf --run "scratch/myfirst --PrintHelp" This will ask Waf to run the scratch/myfirst script and pass the command line argument --PrintHelp to the script. The quotes are required to sort out which program gets which argument. The command line parser will now see the --PrintHelp argument and respond with, Entering directory repos/ns-3-allinone/ns-3-dev/build Build finished successfully (00:00:00) --PrintHelp: Print this help message. --PrintGroups: Print the list of groups. --PrintTypeIds: Print all TypeIds. --PrintGroup=[group]: Print all TypeIds of group. --PrintAttributes=[typeid]: Print all attributes of typeid. --PrintGlobals: Print the list of globals. Lets focus on the --PrintAttributes option. We have already hinted at the ns-3 Attribute system while walking through the first.cc script. We looked at the following lines of code, PointToPointHelper pointToPoint; pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps")); pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms")); and mentioned that DataRate was actually an Attribute of the PointToPointNetDevice. Lets use the command line argument parser to take a look at the Attributes of the PointToPointNetDevice. The help listing says that we should provide a TypeId. This corresponds to the class name of the class to which the Attributes belong. In this case it will be ns3::PointToPointNetDevice. Lets go ahead and type in, ./waf --run "scratch/myfirst --PrintAttributes=ns3::PointToPointNetDevice" The system will print out all of the Attributes of this kind of net device. Among the Attributes you will see listed is, --ns3::PointToPointNetDevice::DataRate=[32768bps]: The default data rate for point to point links This is the default value that will be used when a PointToPointNetDevice is created in the system. We overrode this default with the Attribute setting in the PointToPointHelper above. Lets use the default values for the point-to-point devices and channels by deleting the SetDeviceAttribute call and the SetChannelAttribute call from the first.cc we have in the scratch directory. Your script should now just declare the PointToPointHelper and not do any set operations as in the following example, ... NodeContainer nodes; nodes.Create (2);
33
PointToPointHelper pointToPoint; NetDeviceContainer devices; devices = pointToPoint.Install (nodes); ... Go ahead and build the new script with Waf (./waf) and lets go back and enable some logging from the UDP echo server application and turn on the time prex. export NS_LOG=UdpEchoServerApplication=level_all|prefix_time If you run the script, you should now see the following output, Build finished successfully (00:00:00) 0s UdpEchoServerApplication:UdpEchoServer() 1s UdpEchoServerApplication:StartApplication() Sent 1024 bytes to 10.1.1.2 2.25732s Received 1024 bytes from 10.1.1.1 2.25732s Echoing packet Received 1024 bytes from 10.1.1.2 10s UdpEchoServerApplication:StopApplication() UdpEchoServerApplication:DoDispose() UdpEchoServerApplication:~UdpEchoServer() Recall that the last time we looked at the simulation time at which the packet was received by the echo server, it was at 2.00369 seconds. 2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1 Now it is receiving the packet at 2.25732 seconds. This is because we just dropped the data rate of the PointToPointNetDevice down to its default of 32768 bits per second from ve megabits per second. If we were to provide a new DataRate using the command line, we could speed our simulation up again. We do this in the following way, according to the formula implied by the help item: ./waf --run "scratch/myfirst --ns3::PointToPointNetDevice::DataRate=5Mbps" This will set the default value of the DataRate Attribute back to ve megabits per second. Are you surprised by the result? It turns out that in order to get the original behavior of the script back, we will have to set the speed-of-light delay of the channel as well. We can ask the command line system to print out the Attributes of the channel just like we did for the net device: ./waf --run "scratch/myfirst --PrintAttributes=ns3::PointToPointChannel" We discover the Delay Attribute of the channel is set in the following way: --ns3::PointToPointChannel::Delay=[0ns]: Transmission delay through the channel We can then set both of these default values through the command line system, ./waf --run "scratch/myfirst --ns3::PointToPointNetDevice::DataRate=5Mbps
34
--ns3::PointToPointChannel::Delay=2ms" in which case we recover the timing we had when we explicitly set the DataRate and Delay in the script: Entering directory repos/ns-3-allinone/ns-3-dev/build Build finished successfully (00:00:00) 0s UdpEchoServerApplication:UdpEchoServer() 1s UdpEchoServerApplication:StartApplication() Sent 1024 bytes to 10.1.1.2 2.00369s Received 1024 bytes from 10.1.1.1 2.00369s Echoing packet Received 1024 bytes from 10.1.1.2 10s UdpEchoServerApplication:StopApplication() UdpEchoServerApplication:DoDispose() UdpEchoServerApplication:~UdpEchoServer() Note that the packet is again received by the server at 2.00369 seconds. We could actually set any of the Attributes used in the script in this way. In particular we could set the UdpEchoClient Attribute MaxPackets to some other value than one. How would you go about that? Give it a try. Remember you have to comment out the place we override the default Attribute in the script. Then you have to rebuild the script using the default. You will also have to nd the syntax for actually setting the new default atribute value using the command line help facility. Once you have this gured out you should be able to control the number of packets echoed from the command line. Since were nice folks, well tell you that your command line should end up looking something like, ./waf --run "scratch/myfirst --ns3::PointToPointNetDevice::DataRate=5Mbps --ns3::PointToPointChannel::Delay=2ms --ns3::UdpEchoClient::MaxPackets=2"
35
... Scroll down to the point in the script where we set the MaxPackets Attribute and change it so that it is set to the variable nPackets instead of the constant 1 as is shown below. echoClient.SetAttribute ("MaxPackets", UintegerValue (nPackets)); Now if you run the script and provide the --PrintHelp argument, you should see your new User Argument listed in the help display. Try, ./waf --run "scratch/myfirst --PrintHelp" Entering directory repos/ns-3-allinone/ns-3-dev/build Build finished successfully (00:00:00) --PrintHelp: Print this help message. --PrintGroups: Print the list of groups. --PrintTypeIds: Print all TypeIds. --PrintGroup=[group]: Print all TypeIds of group. --PrintAttributes=[typeid]: Print all attributes of typeid. --PrintGlobals: Print the list of globals. User Arguments: --nPackets: Number of packets to echo If you want to specify the number of packets to echo, you can now do so by setting the --nPackets argument in the command line, ./waf --run "scratch/myfirst --nPackets=2" You should now see Entering directory repos/ns-3-allinone/ns-3-dev/build Build finished successfully (00:00:00) 0s UdpEchoServerApplication:UdpEchoServer() 1s UdpEchoServerApplication:StartApplication() Sent 1024 bytes to 10.1.1.2 2.25732s Received 1024 bytes from 10.1.1.1 2.25732s Echoing packet Received 1024 bytes from 10.1.1.2 Sent 1024 bytes to 10.1.1.2 3.25732s Received 1024 bytes from 10.1.1.1 3.25732s Echoing packet Received 1024 bytes from 10.1.1.2 10s UdpEchoServerApplication:StopApplication() UdpEchoServerApplication:DoDispose() UdpEchoServerApplication:~UdpEchoServer() You have now echoed two packets. You can see that if you are an ns-3 user, you can use the command line argument system to control global values and Attributes. If you are a model author, you can add new Attributes to your Objects and they will automatically be available for setting by your users through the command line system. If you are a script author, you can add new variables to your scripts and hook them into the command line system quite painlessly.
36
37
38
d: A packet was dropped, typically because the queue was full; r: A packet was received by the net device. Lets take a more detailed view of the rst line in the trace le. Ill break it down into sections (indented for clarity) with a two digit reference number on the left side: 00 01 02 03 04 05 06 07 08 09 10 + 2 /NodeList/0/DeviceList/0/$ns3::PointToPointNetDevice/TxQueue/Enqueue ns3::PppHeader ( Point-to-Point Protocol: IP (0x0021)) ns3::Ipv4Header ( tos 0x0 ttl 64 id 0 protocol 17 offset 0 flags [none] length: 1052 10.1.1.1 > 10.1.1.2) ns3::UdpHeader ( length: 1032 49153 > 9) Payload (size=1024)
The rst line of this expanded trace event (reference number 00) is the operation. We have a + character, so this corresponds to an enqueue operation on the transmit queue. The second line (reference 01) is the simulation time expressed in seconds. You may recall that we asked the UdpEchoClientApplication to start sending packets at two seconds. Here we see conrmation that this is, indeed, happening. The next line of the example trace (reference 02) tell us which trace source originated this event (expressed in the tracing namespace). You can think of the tracing namespace somewhat like you would a lesystem namespace. The root of the namespace is the NodeList. This corresponds to a container managed in the ns-3 core code that contains all of the nodes that are created in a script. Just as a lesystem may have directories under the root, we may have node numbers in the NodeList. The string /NodeList/0 therefore refers to the zeroth node in the NodeList which we typically think of as node 0. In each node there is a list of devices that have been installed. This list appears next in the namespace. You can see that this trace event comes from DeviceList/0 which is the zeroth device installed in the node. The next string, $ns3::PointToPointNetDevice tells you what kind of device is in the zeroth position of the device list for node zero. Recall that the operation + found at reference 00 meant that an enqueue operation happened on the transmit queue of the device. This is reected in the nal segments of the trace path which are TxQueue/Enqueue. The remaining lines in the trace should be fairly intuitive. References 03-04 indicate that the packet is encapsulated in the point-to-point protocol. References 05-07 show that the packet has an IP version four header and has originated from IP address 10.1.1.1 and is destined for 10.1.1.2. References 08-09 show that this packet has a UDP header and, nally, reference 10 shows that the payload is the expected 1024 bytes. The next line in the trace le shows the same packet being dequeued from the transmit queue on the same node. The Third line in the trace le shows the packet being received by the net device on the node with the echo server. I have reproduced that event below. 00 r
39
01 2.25732 02 /NodeList/1/DeviceList/0/$ns3::PointToPointNetDevice/MacRx 03 ns3::PppHeader ( 04 Point-to-Point Protocol: IP (0x0021)) 05 ns3::Ipv4Header ( 06 tos 0x0 ttl 64 id 0 offset 0 flags [none] 07 length: 1052 10.1.1.1 > 10.1.1.2) 08 ns3::UdpHeader ( 09 length: 1032 49153 > 9) 10 Payload (size=1024) Notice that the trace operation is now r and the simulation time has increased to 2.25732 seconds. If you have been following the tutorial steps closely this means that you have left the DataRate of the net devices and the channel Delay set to their default values. This time should be familiar as you have seen it before in a previous section. The trace source namespace entry (reference 02) has changed to reect that this event is coming from node 1 (/NodeList/1) and the packet reception trace source (/MacRx). It should be quite easy for you to follow the progress of the packet through the topology by looking at the rest of the traces in the le.
40
41
6 Building Topologies
6.1 Building a Bus Network Topology
In this section we are going to expand our mastery of ns-3 network devices and channels to cover an example of a bus network. Ns-3 provides a net device and channel we call CSMA (Carrier Sense Multiple Access). The ns-3 CSMA device models a simple network in the spirit of Ethernet. A real Ethernet uses CSMA/CD (Carrier Sense Multiple Access with Collision Detection) scheme with exponentially increasing backo to contend for the shared transmission medium. The ns-3 CSMA device and channel models only a subset of this. Just as we have seen point-to-point topology helper objects when constructing pointto-point topologies, we will see equivalent CSMA topology helpers in this section. The appearance and operation of these helpers should look quite familiar to you. We provide an example script in our examples directory. This script builds on the first.cc script and adds a CSMA network to the point-to-point simulation weve already considered. Go ahead and open examples/second.cc in your favorite editor. You will have already seen enough ns-3 code to understand most of what is going on in this example, but we will go over the entire script and examine some of the output. Just as in the first.cc example (and in all ns-3 examples) the le begins with an emacs mode line and some GPL boilerplate. The actual code begins by loading module include les just as was done in the first.cc example. #include #include #include #include #include "ns3/core-module.h" "ns3/simulator-module.h" "ns3/node-module.h" "ns3/helper-module.h" "ns3/global-routing-module.h"
One thing that can be surprisingly useful is a small bit of ASCII art that shows a cartoon of the network topology constructed in the example. You will nd a similar drawing in most of our examples. In this case, you can see that we are going to extend our point-to-point example (the link between the nodes n0 and n1 below) by hanging a bus network o of the right side. Notice that this is the default network topology since you can actually vary the number of nodes created on the LAN. If you set nCsma to one, there will be a total of two nodes on the LAN (CSMA channel) one required node and one extra node. By default there are three extra nodes as seen below: // Default Network Topology // // 10.1.1.0 // n0 -------------- n1 n2 n3 n4 // point-to-point | | | | // ================ // LAN 10.1.2.0
42
Then the ns-3 namespace is used and a logging component is dened. This is all just as it was in first.cc, so there is nothing new yet. using namespace ns3; NS_LOG_COMPONENT_DEFINE ("SecondScriptExample"); The main program begins with a slightly dierent twist. We use a verbose ag to determine whether or not the UdpEchoClientApplication and UdpEchoServerApplication logging components are enabled. This ag defaults to true (the logging components are enabled) but allows us to turn o logging during regression testing of this example. You will see some familiar code that will allow you to change the number of devices on the CSMA network via command line argument. We did something similar when we allowed the number of packets sent to be changed in the section on command line arguments. The last line makes sure you have at least one extra node. The code consists of variations of previously covered API so you should be entirely comfortable with the following code at this point in the tutorial. bool verbose = true; uint32_t nCsma = 3; CommandLine cmd; cmd.AddValue (nCsma, Number of \"extra\" CSMA nodes/devices, nCsma); cmd.AddValue (verbose, Tell echo applications to log if true, verbose); cmd.Parse (argc,argv); if (verbose) { LogComponentEnable(UdpEchoClientApplication, LOG_LEVEL_INFO); LogComponentEnable(UdpEchoServerApplication, LOG_LEVEL_INFO); } nCsma = nCsma == 0 ? 1 : nCsma; The next step is to create two nodes that we will connect via the point-to-point link. The NodeContainer is used to do this just as was done in first.cc. NodeContainer p2pNodes; p2pNodes.Create (2); Next, we declare another NodeContainer to hold the nodes that will be part of the bus (CSMA) network. First, we just instantiate the container object itself. NodeContainer csmaNodes; csmaNodes.Add (p2pNodes.Get (1)); csmaNodes.Create (nCsma); The next line of code Gets the rst node (as in having an index of one) from the point-topoint node container and adds it to the container of nodes that will get CSMA devices. The node in question is going to end up with a point-to-point device and a CSMA device. We then create a number of extra nodes that compose the remainder of the CSMA network.
43
Since we already have one node in the CSMA network the one that will have both a pointto-point and CSMA net device, the number of extra nodes means the number nodes you desire in the CSMA section minus one. The next bit of code should be quite familiar by now. We instantiate a PointToPointHelper and set the associated default Attributes so that we create a ve megabit per second transmitter on devices created using the helper and a two millisecond delay on channels created by the helper. PointToPointHelper pointToPoint; pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps")); pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms")); NetDeviceContainer p2pDevices; p2pDevices = pointToPoint.Install (p2pNodes); We then instantiate a NetDeviceContainer to keep track of the point-to-point net devices and we Install devices on the point-to-point nodes. We mentioned above that you were going to see a helper for CSMA devices and channels, and the next lines introduce them. The CsmaHelper works just like a PointToPointHelper, but it creates and connects CSMA devices and channels. In the case of a CSMA device and channel pair, notice that the data rate is specied by a channel Attribute instead of a device Attribute. This is because a real CSMA network does not allow one to mix, for example, 10Base-T and 100Base-T devices on a given channel. We rst set the data rate to 100 megabits per second, and then set the speed-of-light delay of the channel to 6560 nano-seconds (arbitrarily chosen as 1 nanosecond per foot over a 100 meter segment). Notice that you can set an Attribute using its native data type. CsmaHelper csma; csma.SetChannelAttribute ("DataRate", StringValue ("100Mbps")); csma.SetChannelAttribute ("Delay", TimeValue (NanoSeconds (6560))); NetDeviceContainer csmaDevices; csmaDevices = csma.Install (csmaNodes); Just as we created a NetDeviceContainer to hold the devices created by the PointToPointHelper we create a NetDeviceContainer to hold the devices created by our CsmaHelper. We call the Install method of the CsmaHelper to install the devices into the nodes of the csmaNodes NodeContainer. We now have our nodes, devices and channels created, but we have no protocol stacks present. Just as in the first.cc script, we will use the InternetStackHelper to install these stacks. InternetStackHelper stack; stack.Install (p2pNodes.Get (0)); stack.Install (csmaNodes); Recall that we took one of the nodes from the p2pNodes container and added it to the csmaNodes container. Thus we only need to install the stacks on the remaining p2pNodes node, and all of the nodes in the csmaNodes container to cover all of the nodes in the simulation.
44
Just as in the first.cc example script, we are going to use the Ipv4AddressHelper to assign IP addresses to our device interfaces. First we use the network 10.1.1.0 to create the two addresses needed for our two point-to-point devices. Ipv4AddressHelper address; address.SetBase ("10.1.1.0", "255.255.255.0"); Ipv4InterfaceContainer p2pInterfaces; p2pInterfaces = address.Assign (p2pDevices); Recall that we save the created interfaces in a container to make it easy to pull out addressing information later for use in setting up the applications. We now need to assign IP addresses to our CSMA device interfaces. The operation works just as it did for the point-to-point case, except we now are performing the operation on a container that has a variable number of CSMA devices remember we made the number of CSMA devices changeable by command line argument. The CSMA devices will be associated with IP addresses from network number 10.1.2.0 in this case, as seen below. address.SetBase ("10.1.2.0", "255.255.255.0"); Ipv4InterfaceContainer csmaInterfaces; csmaInterfaces = address.Assign (csmaDevices); Now we have a topology built, but we need applications. This section is going to be fundamentally similar to the applications section of first.cc but we are going to instantiate the server on one of the nodes that has a CSMA node and the client on the node having only a point-to-point device. First, we set up the echo server. We create a UdpEchoServerHelper and provide a required Attribute value to the constructor which is the server port number. Recall that this port can be changed later using the SetAttribute method if desired, but we require it to be provided to the constructor. UdpEchoServerHelper echoServer (9); ApplicationContainer serverApps = echoServer.Install (csmaNodes.Get (nCsma)); serverApps.Start (Seconds (1.0)); serverApps.Stop (Seconds (10.0)); Recall that the csmaNodes NodeContainer contains one of the nodes created for the point-to-point network and nCsma extra nodes. What we want to get at is the last of the extra nodes. The zeroth entry of the csmaNodes container will be the point-to-point node. The easy way to think of this, then, is if we create one extra CSMA node, then it will be at index one of the csmaNodes container. By induction, if we create nCsma extra nodes the last one will be at index nCsma. You see this exhibited in the Get of the rst line of code. The client application is set up exactly as we did in the first.cc example script. Again, we provide required Attributes to the UdpEchoClientHelper in the constructor (in this case the remote address and port). We tell the client to send packets to the server we just installed on the last of the extra CSMA nodes. We install the client on the leftmost point-to-point node seen in the topology illustration. UdpEchoClientHelper echoClient (csmaInterfaces.GetAddress (nCsma), 9); echoClient.SetAttribute ("MaxPackets", UintegerValue (1)); echoClient.SetAttribute ("Interval", TimeValue (Seconds (1.)));
45
echoClient.SetAttribute ("PacketSize", UintegerValue (1024)); ApplicationContainer clientApps = echoClient.Install (p2pNodes.Get (0)); clientApps.Start (Seconds (2.0)); clientApps.Stop (Seconds (10.0)); Since we have actually built an internetwork here, we need some form of internetwork routing. ns-3 provides what we call a global route manager to set up the routing tables on nodes. This route manager has a global function that runs though the nodes created for the simulation and does the hard work of setting up routing for you. Basically, what happens is that each node behaves as if it were an OSPF router that communicates instantly and magically with all other routers behind the scenes. Each node generates link advertisements and communicates them directly to a global route manager which uses this global information to construct the routing tables for each node. Setting up this form of routing is a one-liner: GlobalRouteManager::PopulateRoutingTables (); Next we enable pcap tracing. The rst line of code to enable pcap tracing in the pointto-point helper should be familiar to you by now. The second line enables pcap tracing in the CSMA helper and there is an extra parameter you havent encountered yet. PointToPointHelper::EnablePcapAll ("second"); CsmaHelper::EnablePcap ("second", csmaDevices.Get (0), true); The CSMA network is a multi-point-to-point network. This means that there can (and are in this case) multiple endpoints on a shared medium. Each of these endpoints has a net device associated with it. There are two basic alternatives to gathering trace information from such a network. One way is to create a trace le for each net device and store only the packets that are emitted or consumed by that net device. Another way is to pick one of the devices and place it in promiscuous mode. That single device then snis the network for all packets and stores them in a single pcap le. This is how tcpdump, for example, works. That nal parameter tells the CSMA helper whether or not to capture packets in promiscuous mode. In this example, we are going to select one of the devices on the CSMA network and ask it to perform a promiscuous sni of the network, thereby emulating what tcpdump would do. If you were on a Linux machine you might do something like tcpdump -i eth0 to get the trace. In this case, we specify the device using csmaDevices.Get(0), which selects the zeroth device in the container. Setting the nal parameter to true enables promiscuous captures. The last section of code just runs and cleans up the simulation just like the first.cc example. Simulator::Run (); Simulator::Destroy (); return 0; } In order to run this example, you have to copy the second.cc example script into the scratch directory and use waf to build just as you did with the first.cc example. If you are in the top-level directory of the repository you would type,
46
cp examples/second.cc scratch/mysecond.cc ./waf Warning: We use the le second.cc as one of our regression tests to verify that it works exactly as we think it should in order to make your tutorial experience a positive one. This means that an executable named second already exists in the project. To avoid any confusion about what you are executing, please do the renaming to mysecond.cc suggested above. If you are following the tutorial religiously (you are, arent you) you will still have the NS LOG variable set, so go ahead and clear that variable and run the program. export NS_LOG= ./waf --run scratch/mysecond #end verbatim Since we have set up the UDP echo applications to log just as we did in @code{first.cc}, you will see similar output when you run the script. @verbatim Entering directory repos/ns-3-allinone/ns-3-dev/build Build finished successfully (00:00:00) Sent 1024 bytes to 10.1.2.4 Received 1024 bytes from 10.1.1.1 Received 1024 bytes from 10.1.2.4 Recall that the rst message, Sent 1024 bytes to 10.1.2.4 is the UDP echo client sending a packet to the server. In this case, the server is on a dierent network (10.1.2.0). The second message, Received 1024 bytes from 10.1.1.1, is from the UDP echo server, generated when it receives the echo packet. The nal message, Received 1024 bytes from 10.1.2.4 is from the echo client, indicating that it has received its echo back from the server. If you now go and look in the top level directory, you will nd two trace les: second-0-0.pcap second-1-0.pcap second-2-0.pcap Lets take a moment to look at the naming of these les. They all have the same form, <name>-<node>-<device>.pcap. For example, the rst le in the listing is second-00.pcap which is the pcap trace from node zero, device zero. This is the point-to-point net device on node zero. The le second-1-0.pcap is the pcap trace for device zero on node one, also a point-to-point net device; and the le second-2-0.pcap is the pcap trace for device zero on node two. If you refer back to the topology illustration at the start of the section, you will see that node zero is the leftmost node of the point-to-point link and node one is the node that has both a point-to-point device and a CSMA device. You will see that node two is the rst extra node on the CSMA network and its device zero was selected as the device to capture the promiscuous-mode trace. Now, lets follow the echo packet through the internetwork. First, do a tcpdump of the trace le for the leftmost point-to-point node node zero. tcpdump -nn -tt -r second-0-0.pcap You should see the contents of the pcap le displayed:
47
reading from file second-0-0.pcap, link-type PPP (PPP) 2.000000 IP 10.1.1.1.49153 > 10.1.2.4.9: UDP, length 1024 2.007602 IP 10.1.2.4.9 > 10.1.1.1.49153: UDP, length 1024 The rst line of the dump indicates that the link type is PPP (point-to-point) which we expect. You then see the echo packet leaving node zero via the device associated with IP address 10.1.1.1 headed for IP address 10.1.2.4 (the rightmost CSMA node). This packet will move over the point-to-point link and be received by the point-to-point net device on node one. Lets take a look: tcpdump -nn -tt -r second-1-0.pcap You should now see the pcap trace output of the other side of the point-to-point link: reading from file second-1-0.pcap, link-type PPP (PPP) 2.003686 IP 10.1.1.1.49153 > 10.1.2.4.9: UDP, length 1024 2.003915 IP 10.1.2.4.9 > 10.1.1.1.49153: UDP, length 1024 Here we see that the link type is also PPP as we would expect. You see the packet from IP address 10.1.1.1 (that was sent at 2.000000 seconds) headed toward IP address 10.1.2.4 appear on this interface. Now, internally to this node, the packet will be forwarded to the CSMA interface and we should see it pop out on that device headed for its ultimate destination. Remember that we selected node 2 as the promiscuous snier node for the CSMA network so lets then look at second-2-0.pcap and see if its there. tcpdump -nn -tt -r second-2-0.pcap You should now see the promiscuous dump of node two, device zero: reading from file second-2-0.pcap, link-type EN10MB (Ethernet) 2.003696 arp who-has 10.1.2.4 (ff:ff:ff:ff:ff:ff) tell 10.1.2.1 2.003707 arp reply 10.1.2.4 is-at 00:00:00:00:00:06 2.003801 IP 10.1.1.1.49153 > 10.1.2.4.9: UDP, length 1024 2.003811 arp who-has 10.1.2.1 (ff:ff:ff:ff:ff:ff) tell 10.1.2.4 2.003822 arp reply 10.1.2.1 is-at 00:00:00:00:00:03 2.003915 IP 10.1.2.4.9 > 10.1.1.1.49153: UDP, length 1024 As you can see, the link type is now Ethernet. Something new has appeared, though. The bus network needs ARP, the Address Resolution Protocol. Node one knows it needs to send the packet to IP address 10.1.2.4, but it doesnt know the MAC address of the corresponding node. It broadcasts on the CSMA network (:::::) asking for the device that has IP address 10.1.2.4. In this case, the rightmost node replies saying it is at MAC address 00:00:00:00:00:06. (Note that node two is not directly involved in this exchange, but is sning the network and reporting all of the trac it sees.) This exchange is seen in the following lines, 2.003696 arp who-has 10.1.2.4 (ff:ff:ff:ff:ff:ff) tell 10.1.2.1 2.003707 arp reply 10.1.2.4 is-at 00:00:00:00:00:06 Then node one, device one goes ahead and sends the echo packet to the UDP echo server at IP address 10.1.2.4. 2.003801 IP 10.1.1.1.49153 > 10.1.2.4.9: UDP, length 1024 The server receives the echo request and turns the packet around trying to send it back to the source. The server knows that this address is on another network that it reaches via
48
IP address 10.1.2.1. This is because we initialized global routing and it has gured all of this out for us. But, the echo server node doesnt know the MAC address of the rst CSMA node, so it has to ARP for it just like the rst CSMA node had to do. 2.003811 arp who-has 10.1.2.1 (ff:ff:ff:ff:ff:ff) tell 10.1.2.4 2.003822 arp reply 10.1.2.1 is-at 00:00:00:00:00:03 The server then sends the echo back to the forwarding node. 2.003915 IP 10.1.2.4.9 > 10.1.1.1.49153: UDP, length 1024 Looking back at the rightmost node of the point-to-point link, tcpdump -nn -tt -r second-1-0.pcap You can now see the echoed packet coming back onto the point-to-point link as the last line of the trace dump. reading from file second-1-0.pcap, link-type PPP (PPP) 2.003686 IP 10.1.1.1.49153 > 10.1.2.4.9: UDP, length 1024 2.003915 IP 10.1.2.4.9 > 10.1.1.1.49153: UDP, length 1024 Lastly, you can look back at the node that originated the echo tcpdump -nn -tt -r second-0-0.pcap and see that the echoed packet arrives back at the source at 2.007602 seconds, reading from file second-0-0.pcap, link-type PPP (PPP) 2.000000 IP 10.1.1.1.49153 > 10.1.2.4.9: UDP, length 1024 2.007602 IP 10.1.2.4.9 > 10.1.1.1.49153: UDP, length 1024 Finally, recall that we added the ability to control the number of CSMA devices in the simulation by command line argument. You can change this argument in the same way as when we looked at changing the number of packets echoed in the first.cc example. Try running the program with the number of extra devices set to four: ./waf --run "scratch/mysecond --nCsma=4" You should now see, Entering directory repos/ns-3-allinone/ns-3-dev/build Build finished successfully (00:00:00) Sent 1024 bytes to 10.1.2.5 Received 1024 bytes from 10.1.1.1 Received 1024 bytes from 10.1.2.5 Notice that the echo server has now been relocated to the last of the CSMA nodes, which is 10.1.2.5 instead of the default case, 10.1.2.4. It is possible that you may not be satised with a trace le generated by a bystander in the CSMA network. You may really want to get a trace from a single device and you may not be interested in any other trac on the network. You can do this, Lets take a look at scratch/mysecond.cc and add that code enabling us to be more specic. ns-3 helpers provide methods that take a node number and device number as parameters. Go ahead and replace the EnablePcap calls with the calls below. PointToPointHelper::EnablePcap ("second", p2pNodes.Get (0)->GetId (), 0); CsmaHelper::EnablePcap ("second", csmaNodes.Get (nCsma)->GetId (), 0, false); CsmaHelper::EnablePcap ("second", csmaNodes.Get (nCsma-1)->GetId (), 0, false);
49
We know that we want to create a pcap le with the base name "second" and we also know that the device of interest in both cases is going to be zero, so those parameters are not really interesting. In order to get the node number, you have two choices: rst, nodes are numbered in a monotonically increasing fashion starting from zero in the order in which you created them. One way to get a node number is to gure this number out manually by contemplating the order of node creation. If you take a look at the network topology illustration at the beginning of the le, we did this for you and you can see that the last CSMA node is going to be node number nCsma + 1. This approach can become annoyingly dicult in larger simulations. An alternate way, which we use here, is to realize that the NodeContainers contain pointers to ns-3 Node Objects. The Node Object has a method called GetId which will return that nodes ID, which is the node number we seek. Lets go take a look at the Doxygen for the Node and locate that method, which is further down in the ns-3 core code than weve seen so far; but sometimes you have to search diligently for useful things. Go to the Doxygen documentation for your release (recall that you can nd it on the project web site). You can get to the Node documentation by looking through at the Classes tab and scrolling down the Class List until you nd ns3::Node. Select ns3::Node and you will be taken to the documentation for the Node class. If you now scroll down to the GetId method and select it, you will be taken to the detailed documentation for the method. Using the GetId method can make determining node numbers much easier in complex topologies. If you build the new script and run the simulation setting nCsma to 100, ./waf --run "scratch/mysecond --nCsma=100" you will see the following output: Entering directory repos/ns-3-allinone/ns-3-dev/build Build finished successfully (00:00:00) Sent 1024 bytes to 10.1.2.101 Received 1024 bytes from 10.1.1.1 Received 1024 bytes from 10.1.2.101 Note that the echo server is now located at 10.1.2.101 which corresponds to having 100 extra CSMA nodes with the echo server on the last one. If you list the pcap les in the top level directory you will see, second-0-0.pcap second-100-0.pcap second-101-0.pcap The trace le second-0-0.pcap is the leftmost point-to-point device which is the echo packet source. The le second-101-0.pcap corresponds to the rightmost CSMA device which is where the echo server resides. You may have noticed that the nal parameter on the call to enable pcap tracing on the echo server node was false. This means that the trace gathered on that node was in non-promiscuous mode. To illustrate the dierence between promiscuous and non-promiscuous traces, we also requested a non-promiscuous trace for the next-to-last node. Go ahead and take a look at the tcpdump for second-10-0.pcap. tcpdump -nn -tt -r second-100-0.pcap
50
You can now see that node 100 is really a bystander in the echo exchange. The only packets that it receives are the ARP requests which are broadcast to the entire CSMA network. reading from file second-100-0.pcap, link-type EN10MB (Ethernet) 2.003696 arp who-has 10.1.2.101 (ff:ff:ff:ff:ff:ff) tell 10.1.2.1 2.003811 arp who-has 10.1.2.1 (ff:ff:ff:ff:ff:ff) tell 10.1.2.101
51
// Wifi 10.1.3.0 // AP // * * * * // | | | | 10.1.1.0 // n5 n6 n7 n0 -------------- n1 n2 n3 n4 // point-to-point | | | | // ================ // LAN 10.1.2.0 You can see that we are adding a new network device to the node on the left side of the point-to-point link that becomes the access point for the wireless network. A number of wireless STA nodes are created to ll out the new 10.1.3.0 network as shown on the left side of the illustration. After the illustration, the ns-3 namespace is used and a logging component is dened. This should all be quite familiar by now. using namespace ns3; NS_LOG_COMPONENT_DEFINE ("ThirdScriptExample"); The main program begins just like second.cc by adding some command line parameters for enabling or disabling logging components and for changing the number of devices created. bool verbose = true; uint32_t nCsma = 3; uint32_t nWifi = 3; CommandLine cmd; cmd.AddValue (nCsma, Number of \"extra\" CSMA nodes/devices, nCsma); cmd.AddValue (nWifi, Number of wifi STA devices, nWifi); cmd.AddValue (verbose, Tell echo applications to log if true, verbose); cmd.Parse (argc,argv); if (verbose) { LogComponentEnable(UdpEchoClientApplication, LOG_LEVEL_INFO); LogComponentEnable(UdpEchoServerApplication, LOG_LEVEL_INFO); } Just as in all of the previous examples, the next step is to create two nodes that we will connect via the point-to-point link. NodeContainer p2pNodes; p2pNodes.Create (2); Next, we see an old friend. We instantiate a PointToPointHelper and set the associated default Attributes so that we create a ve megabit per second transmitter on devices created using the helper and a two millisecond delay on channels created by the helper. We then Intall the devices on the nodes and the channel between them. PointToPointHelper pointToPoint; pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
52
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms")); NetDeviceContainer p2pDevices; p2pDevices = pointToPoint.Install (p2pNodes); Next, we declare another NodeContainer to hold the nodes that will be part of the bus (CSMA) network. NodeContainer csmaNodes; csmaNodes.Add (p2pNodes.Get (1)); csmaNodes.Create (nCsma); The next line of code Gets the rst node (as in having an index of one) from the point-topoint node container and adds it to the container of nodes that will get CSMA devices. The node in question is going to end up with a point-to-point device and a CSMA device. We then create a number of extra nodes that compose the remainder of the CSMA network. We then instantiate a CsmaHelper and set its Attributes as we did in the previous example. We create a NetDeviceContainer to keep track of the created CSMA net devices and then we Install CSMA devices on the selected nodes. CsmaHelper csma; csma.SetChannelAttribute ("DataRate", StringValue ("100Mbps")); csma.SetChannelAttribute ("Delay", TimeValue (NanoSeconds (6560))); NetDeviceContainer csmaDevices; csmaDevices = csma.Install (csmaNodes); Next, we are going to create the nodes that will be part of the Wi network. We are going to create a number of station nodes as specied by the command line argument, and we are going to use the leftmost node of the point-to-point link as the node for the access point. NodeContainer wifiStaNodes; wifiStaNodes.Create (nWifi); NodeContainer wifiApNode = p2pNodes.Get (0); The next bit of code constructs the wi devices and the interconnection channel between these wi nodes. First, we congure the PHY and channel helpers: YansWifiChannelHelper channel = YansWifiChannelHelper::Default (); YansWifiPhyHelper phy = YansWifiPhyHelper::Default (); For simplicity, this code uses the default PHY layer conguration and channel models which are documented in the API doxygen documentation for the YansWifiChannelHelper::Default and YAnsWifiPhyHelper::Default methods. Once these objects are created, we create a channel object and associate it to our PHY layer object manager to make sure that all the PHY objects created layer by the YansWifiPhyHelper all share the same underlying channel, that is, they share the same wireless medium and can communication and interfere: phy.SetChannel (channel.Create ()); Once the PHY helper is congured, we can focus on the MAC layer: WifiHelper wifi = WifiHelper::Default (); wifi.SetRemoteStationManager ("ns3::AarfWifiManager");
53
The SetRemoteStationManager method tells the helper the type of rate control algorithm to use. Here, it is asking the helper to use the AARF algorithm details are, of course, available in Doxygen. Next, we congure the SSID of the infrastructure network we want to setup and make sure that our stations dont perform active probing: Ssid ssid = Ssid ("ns-3-ssid"); wifi.SetMac ("ns3::NqstaWifiMac", "Ssid", SsidValue (ssid), "ActiveProbing", BooleanValue (false)); This code rst creates an 802.11 service set identier (SSID) object that will be used to set the value of the Ssid Attribute of the MAC layer implementation. The particular kind of MAC layer is specied by Attribute as being of the "ns3::NqstaWiMac" type. This means that the MAC will use a non-QoS station (nqsta) state machine. Finally, the ActiveProbing Attribute is set to false. This means that probe requests will not be sent by MACs created by this helper. Once all the station-specic parameters are fully congured, both at the MAC and PHY layers, we can invoke our now-familiar Install method to create the wi devices of these stations: NetDeviceContainer staDevices; staDevices = wifi.Install (phy, wifiStaNodes); We have congured Wi for all of our STA nodes, and now we need to congure the AP (access point) node. We begin this process by changing the default Attributes of the WifiHelper to reect the requirements of the AP. wifi.SetMac ("ns3::NqapWifiMac", "Ssid", SsidValue (ssid), "BeaconGeneration", BooleanValue (true), "BeaconInterval", TimeValue (Seconds (2.5))); In this case, the WifiHelper is going to create MAC layers of the ns3::NqapWiMac (Non-Qos Access Point) type. We set the BeaconGeneration Attribute to true and also set an interval between beacons of 2.5 seconds. The next lines create the single AP which shares the same set of PHY-level Attributes (and channel) as the stations: NetDeviceContainer apDevices; apDevices = wifi.Install (phy, wifiApNode); Now, we are going to add mobility models. We want the STA nodes to be mobile, wandering around inside a bounding box, and we want to make the AP node stationary. We use the MobilityHelper to make this easy for us. First, we instantiate a MobilityHelper object and set some Attributes controlling the position allocator functionality. MobilityHelper mobility; mobility.SetPositionAllocator ("ns3::GridPositionAllocator", "MinX", DoubleValue (0.0), "MinY", DoubleValue (0.0), "DeltaX", DoubleValue (5.0),
54
"DeltaY", DoubleValue (10.0), "GridWidth", UintegerValue (3), "LayoutType", StringValue ("RowFirst")); This code tells the mobility helper to use a two-dimensional grid to initially place the STA nodes. Feel free to explore the Doxygen for class ns3::GridPositionAllocator to see exactly what is being done. We have arranged our nodes on an initial grid, but now we need to tell them how to move. We choose the RandomWalk2dMobilityModel which has the nodes move in a random direction at a random speed around inside a bounding box. mobility.SetMobilityModel ("ns3::RandomWalk2dMobilityModel", "Bounds", RectangleValue (Rectangle (-50, 50, -50, 50))); We now tell the MobilityHelper to install the mobility models on the STA nodes. mobility.Install (wifiStaNodes); We want the access point to remain in a xed position during the simulation. We accomplish this by setting the mobility model for this node to be the ns3::ConstantPositionMobilityModel: mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); mobility.Install (wifiApNode); We now have our nodes, devices and channels created, and mobility models chosen for the Wi nodes, but we have no protocol stacks present. Just as we have done previously many times, we will use the InternetStackHelper to install these stacks. InternetStackHelper stack; stack.Install (csmaNodes); stack.Install (wifiApNode); stack.Install (wifiStaNodes); Just as in the second.cc example script, we are going to use the Ipv4AddressHelper to assign IP addresses to our device interfaces. First we use the network 10.1.1.0 to create the two addresses needed for our two point-to-point devices. Then we use network 10.1.2.0 to assign addresses to the CSMA network and then we assign addresses from network 10.1.3.0 to both the STA devices and the AP on the wireless network. Ipv4AddressHelper address; address.SetBase ("10.1.1.0", "255.255.255.0"); Ipv4InterfaceContainer p2pInterfaces; p2pInterfaces = address.Assign (p2pDevices); address.SetBase ("10.1.2.0", "255.255.255.0"); Ipv4InterfaceContainer csmaInterfaces; csmaInterfaces = address.Assign (csmaDevices); address.SetBase ("10.1.3.0", "255.255.255.0"); address.Assign (staDevices); address.Assign (apDevices); We put the echo server on the rightmost node in the illustration at the start of the le. We have done this before.
55
UdpEchoServerHelper echoServer (9); ApplicationContainer serverApps = echoServer.Install (csmaNodes.Get (nCsma)); serverApps.Start (Seconds (1.0)); serverApps.Stop (Seconds (10.0)); And we put the echo client on the last STA node we created, pointing it to the server on the CSMA network. We have also seen similar operations before. UdpEchoClientHelper echoClient (csmaInterfaces.GetAddress (nCsma), 9); echoClient.SetAttribute ("MaxPackets", UintegerValue (1)); echoClient.SetAttribute ("Interval", TimeValue (Seconds (1.))); echoClient.SetAttribute ("PacketSize", UintegerValue (1024)); ApplicationContainer clientApps = echoClient.Install (wifiStaNodes.Get (nWifi - 1)); clientApps.Start (Seconds (2.0)); clientApps.Stop (Seconds (10.0)); Since we have built an internetwork here, we need to enable internetwork routing just as we did in the second.cc example script. GlobalRouteManager::PopulateRoutingTables (); One thing that can surprise some users is the fact that the simulation we just created will never naturally stop. This is because we asked the wireless access point to generate beacons. It will generate beacons forever, so we must tell the simulator to stop even though it may have beacon generation events scheduled. The following line of code tells the simulator to stop so that we dont simulate beacons forever and enter what is essentially an endless loop. Simulator::Stop (Seconds (10.0)); We create just enough tracing to cover all three networks: PointToPointHelper::EnablePcapAll ("third"); YansWifiPhyHelper::EnablePcap ("third", apDevices.Get (0)); CsmaHelper::EnablePcap ("third", csmaDevices.Get (0), true); These three lines of code will start pcap tracing on both of the point-to-point nodes that serves as our backbone, will start a promiscuous (monitor) mode trace on the Wi network, and will start a promiscuous trace on the CSMA network. This will let us see all of the trac with a minimum number of trace les. Finally, we actually run the simulation, clean up and then exit the program. Simulator::Run (); Simulator::Destroy (); return 0; } In order to run this example, you have to copy the third.cc example script into the scratch directory and use Waf to build just as you did with the second.cc example. If you are in the top-level directory of the repository you would type, cp examples/third.cc scratch/mythird.cc ./waf
56
./waf --run scratch/mythird Since we have set up the UDP echo applications just as we did in the second.cc script, you will see similar output. Entering directory repos/ns-3-allinone-dev/ns-3-dev/build Build finished successfully (00:00:00) Sent 1024 bytes to 10.1.2.4 Received 1024 bytes from 10.1.3.3 Received 1024 bytes from 10.1.2.4 Recall that the rst message, Sent 1024 bytes to 10.1.2.4 is the UDP echo client sending a packet to the server. In this case, the client is on the wireless network (10.1.3.0). The second message, Received 1024 bytes from 10.1.3.3, is from the UDP echo server, generated when it receives the echo packet. The nal message, Received 1024 bytes from 10.1.2.4 is from the echo client, indicating that it has received its echo back from the server. If you now go and look in the top level directory, you will nd four trace les, two from node zero and two from node one: third-0-0.pcap third-0-1.pcap third-1-0.pcap third-1-1.pcap The le third-0-0.pcap corresponds to the point-to-point device on node zero the left side of the backbone. The le third-1-0.pcap corresponds to the point-to-point device on node one the right side of the backbone. The le third-0-1.pcap will be the promiscuous (monitor mode) trace from the Wi network and the le third-1-1.pcap will be the promiscuous trace from the CSMA network. Can you verify this by inspecting the code? Since the echo client is on the Wi network, lets start there. Lets take a look at the promiscuous (monitor mode) trace we captured on that network. tcpdump -nn -tt -r third-0-1.pcap You should see some wi-looking contents you havent seen here before: reading from file third-0-1.pcap, link-type IEEE802_11 (802.11) 0.000025 Beacon () [6.0* 9.0 12.0 18.0 24.0 36.0 48.0 54.0 Mbit] IBSS 0.000263 Assoc Request () [6.0 9.0 12.0 18.0 24.0 36.0 48.0 54.0 Mbit] 0.000279 Acknowledgment RA:00:00:00:00:00:07 0.000357 Assoc Response AID(0) :: Succesful 0.000501 Acknowledgment RA:00:00:00:00:00:0a 0.000748 Assoc Request () [6.0 9.0 12.0 18.0 24.0 36.0 48.0 54.0 Mbit] 0.000764 Acknowledgment RA:00:00:00:00:00:08 0.000842 Assoc Response AID(0) :: Succesful 0.000986 Acknowledgment RA:00:00:00:00:00:0a 0.001242 Assoc Request () [6.0 9.0 12.0 18.0 24.0 36.0 48.0 54.0 Mbit] 0.001258 Acknowledgment RA:00:00:00:00:00:09 0.001336 Assoc Response AID(0) :: Succesful 0.001480 Acknowledgment RA:00:00:00:00:00:0a 2.000112 arp who-has 10.1.3.4 (ff:ff:ff:ff:ff:ff) tell 10.1.3.3 2.000128 Acknowledgment RA:00:00:00:00:00:09 2.000206 arp who-has 10.1.3.4 (ff:ff:ff:ff:ff:ff) tell 10.1.3.3 2.000487 arp reply 10.1.3.4 is-at 00:00:00:00:00:0a
57
2.000659 2.002169 2.002185 2.009771 2.010029 2.010045 2.010231 2.011767 2.500000 5.000000 7.500000
Acknowledgment RA:00:00:00:00:00:0a IP 10.1.3.3.49153 > 10.1.2.4.9: UDP, length 1024 Acknowledgment RA:00:00:00:00:00:09 arp who-has 10.1.3.3 (ff:ff:ff:ff:ff:ff) tell 10.1.3.4 arp reply 10.1.3.3 is-at 00:00:00:00:00:09 Acknowledgment RA:00:00:00:00:00:09 IP 10.1.2.4.9 > 10.1.3.3.49153: UDP, length 1024 Acknowledgment RA:00:00:00:00:00:0a Beacon () [6.0* 9.0 12.0 18.0 24.0 36.0 48.0 54.0 Mbit] IBSS Beacon () [6.0* 9.0 12.0 18.0 24.0 36.0 48.0 54.0 Mbit] IBSS Beacon () [6.0* 9.0 12.0 18.0 24.0 36.0 48.0 54.0 Mbit] IBSS
You can see that the link type is now 802.11 as you would expect. You can probably understand what is going on and nd the IP echo request and response packets in this trace. We leave it as an exercise to completely parse the trace dump. Now, look at the pcap le of the right side of the point-to-point link, tcpdump -nn -tt -r third-0-0.pcap Again, you should see some familiar looking contents: reading from file third-0-0.pcap, link-type PPP (PPP) 2.002169 IP 10.1.3.3.49153 > 10.1.2.4.9: UDP, length 1024 2.009771 IP 10.1.2.4.9 > 10.1.3.3.49153: UDP, length 1024 This is the echo packet going from left to right (from Wi to CSMA) and back again across the point-to-point link. Now, look at the pcap le of the right side of the point-to-point link, tcpdump -nn -tt -r third-1-0.pcap Again, you should see some familiar looking contents: reading from file third-1-0.pcap, link-type PPP (PPP) 2.005855 IP 10.1.3.3.49153 > 10.1.2.4.9: UDP, length 1024 2.006084 IP 10.1.2.4.9 > 10.1.3.3.49153: UDP, length 1024 This is also the echo packet going from left to right (from Wi to CSMA) and back again across the point-to-point link with slightly dierent timings as you might expect. The echo server is on the CSMA network, lets look at the promiscuous trace there: tcpdump -nn -tt -r third-1-1.pcap You should see some familiar looking contents: reading from file third-1-1.pcap, link-type EN10MB (Ethernet) 2.005855 arp who-has 10.1.2.4 (ff:ff:ff:ff:ff:ff) tell 10.1.2.1 2.005877 arp reply 10.1.2.4 is-at 00:00:00:00:00:06 2.005877 IP 10.1.3.3.49153 > 10.1.2.4.9: UDP, length 1024 2.005980 arp who-has 10.1.2.1 (ff:ff:ff:ff:ff:ff) tell 10.1.2.4 2.005980 arp reply 10.1.2.1 is-at 00:00:00:00:00:03 2.006084 IP 10.1.2.4.9 > 10.1.3.3.49153: UDP, length 1024 This should be easily understood. If youve forgotten, go back and look at the discussion in second.cc. This is the same sequence.
58
Now, we spent a lot of time setting up mobility models for the wireless network and so it would be a shame to nish up without even showing that the STA nodes are actually moving around. Lets do this by hooking into the MobilityModel course change trace source. This is usually considered a fairly advanced topic, but lets just go for it. As mentioned in the Tweaking ns-3 section, the ns-3 tracing system is divided into trace sources and trace sinks, and we provide functions to connect the two. We will use the mobility model predened course change trace source to originate the trace events. We will need to write a trace sink to connect to that source that will display some pretty information for us. Despite its reputation as being dicult, its really quite simple. Just before the main program of the scratch/mythird.cc script, add the following function: void CourseChange (std::string context, Ptr<const MobilityModel> model) { Vector position = model->GetPosition (); NS_LOG_UNCOND (context << " x = " << position.x << ", y = " << position.y); } This code just pulls the position information from the mobility model and unconditionally logs the x and y position of the node. We are going to arrange for this function to be called every time the wireless node with the echo client changes its position. We do this using the Config::Connect function. Add the following lines of code to the script just before the Simulator::Run call. std::ostringstream oss; oss << "/NodeList/" << wifiStaNodes.Get (nWifi - 1)->GetId () << "/$ns3::MobilityModel/CourseChange"; Config::Connect (oss.str (), MakeCallback (&CourseChange)); What we do here is to create a string containing the tracing namespace path of the event to which we want to connect. First, we have to gure out which node it is we want using the GetId method as described earlier. In the case of the default number of CSMA and wireless nodes, this turns out to be node seven and the tracing namespace path to the mobility model would look like, /NodeList/7/$ns3::MobilityModel/CourseChange Based on the discussion in the tracing section, you can easily infer that this trace path references the seventh node in the NodeList. It species what is called an aggregated object of type ns3::MobilityModel. The dollar sign prex implies that the MobilityModel is aggregated to node seven. The last component of the path means that we are hooking into the CourseChange event of that model. We make a connection between the trace source in node seven with our trace sink by calling Config::Connect and passing this namespace path. Once this is done, every course change event on node seven will be hooked into our trace sink, which will in turn print out the new position. If you now run the simulation, you will see the course changes displayed as they happen.
59
Build finished successfully (00:00:01) /NodeList/7/$ns3::MobilityModel/CourseChange x = 10, y = 0 /NodeList/7/$ns3::MobilityModel/CourseChange x = 9.41539, y = -0.811313 /NodeList/7/$ns3::MobilityModel/CourseChange x = 8.46199, y = -1.11303 /NodeList/7/$ns3::MobilityModel/CourseChange x = 7.52738, y = -1.46869 /NodeList/7/$ns3::MobilityModel/CourseChange x = 6.67099, y = -1.98503 /NodeList/7/$ns3::MobilityModel/CourseChange x = 5.6835, y = -2.14268 /NodeList/7/$ns3::MobilityModel/CourseChange x = 4.70932, y = -1.91689 Sent 1024 bytes to 10.1.2.4 Received 1024 bytes from 10.1.3.3 Received 1024 bytes from 10.1.2.4 /NodeList/7/$ns3::MobilityModel/CourseChange x = 5.53175, y = -2.48576 /NodeList/7/$ns3::MobilityModel/CourseChange x = 4.58021, y = -2.17821 /NodeList/7/$ns3::MobilityModel/CourseChange x = 4.18915, y = -1.25785 /NodeList/7/$ns3::MobilityModel/CourseChange x = 4.7572, y = -0.434856 /NodeList/7/$ns3::MobilityModel/CourseChange x = 4.62404, y = 0.556238 /NodeList/7/$ns3::MobilityModel/CourseChange x = 4.74127, y = 1.54934 /NodeList/7/$ns3::MobilityModel/CourseChange x = 5.73934, y = 1.48729 /NodeList/7/$ns3::MobilityModel/CourseChange x = 6.18521, y = 0.59219 /NodeList/7/$ns3::MobilityModel/CourseChange x = 6.58121, y = 1.51044 /NodeList/7/$ns3::MobilityModel/CourseChange x = 7.27897, y = 2.22677 /NodeList/7/$ns3::MobilityModel/CourseChange x = 6.42888, y = 1.70014 /NodeList/7/$ns3::MobilityModel/CourseChange x = 7.40519, y = 1.91654 /NodeList/7/$ns3::MobilityModel/CourseChange x = 6.51981, y = 1.45166 /NodeList/7/$ns3::MobilityModel/CourseChange x = 7.34588, y = 2.01523 /NodeList/7/$ns3::MobilityModel/CourseChange x = 7.81046, y = 2.90077 /NodeList/7/$ns3::MobilityModel/CourseChange x = 6.89186, y = 3.29596 /NodeList/7/$ns3::MobilityModel/CourseChange x = 7.46617, y = 2.47732 /NodeList/7/$ns3::MobilityModel/CourseChange x = 7.05492, y = 1.56579 /NodeList/7/$ns3::MobilityModel/CourseChange x = 8.00393, y = 1.25054 /NodeList/7/$ns3::MobilityModel/CourseChange x = 7.00968, y = 1.35768 /NodeList/7/$ns3::MobilityModel/CourseChange x = 7.33503, y = 2.30328 /NodeList/7/$ns3::MobilityModel/CourseChange x = 7.18682, y = 3.29223 /NodeList/7/$ns3::MobilityModel/CourseChange x = 7.96865, y = 2.66873 If you are feeling brave, there is a list of all trace sources in the ns-3 Doxygen which you can nd in the Modules tab. Under the core section, you will nd a link to The list of all trace sources. You may nd it interesting to try and hook some of these traces yourself. Additionally in the Modules documentation, there is a link to The list of all attributes. You can set the default value of any of these Attributes via the command line as we have previously discussed. We have just scratched the surface of ns-3 in this tutorial, but we hope we have covered enough to get you started doing useful work. The ns-3 development team.
A
Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 ascii trace dequeue operation . . . . . . . . . . . . . . . . . 37 ascii trace drop operation . . . . . . . . . . . . . . . . . . . . 37 ascii trace enqueue operation . . . . . . . . . . . . . . . . . 37 ascii trace receive operation . . . . . . . . . . . . . . . . . . 37 ASCII tracing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
N
net device number . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 NetDevice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Node . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14