ns-3 Design Overview

ns-3 project http://www.nsnam.org/ feedback: [email protected]

September 17, 2006

Introduction

This ns-3 design document is intended to document the technical goals, software architecture, implementation choices, and interfaces of the simulator. It provides a place to record design agreements or archive design decisions for future reference. It is expected that this document can evolve or fork to become an “ns-3 Developers Guide” at some point in the future.

This documentis written in Latex and is to be maintainedin revision control on the ns-3 code server. Changes to the document should be discussed on the [email protected] mailing list. Contents

1 Functional Overview 3 1.1 Goals ...... 3 1.2 Userexperience...... 3 1.2.1 Installation ...... 3 1.2.2 Userinterface...... 3 1.2.3 Scenariodefinition ...... 4 1.2.4 Capabilities...... 4 1.3 Documentation ...... 4 1.4 RequirementsfromCollaborators...... 4 1.4.1 VINI ...... 5 1.4.2 Emulab...... 5 1.4.3 NetworkSimulationCradle ...... 5 1.4.4 Largescalesimulations...... 5

2 Technical Overview 6 2.1 Basics...... 6 2.2 High-levelarchitecture ...... 7 2.3 Sourcecodeorganization ...... 8 2.4 Buildenvironment ...... 8 2.4.1 SConsoverview ...... 9 2.4.2 Options...... 9 2.4.3 Buildtargets ...... 9 2.4.4 Buildoutput ...... 10

3 ns-3 Coding Standard 11 3.1 Introduction...... 11 3.2 Recommendations...... 11 3.2.1 naming ...... 11 3.2.2 Memorymanagement ...... 12 3.2.3 Templates...... 12 3.3 Standards ...... 12 3.3.1 General...... 12 3.3.2 Commenting ...... 13 3.3.3 NamingConventions...... 14 3.3.4 StatementFormatting...... 15 3.3.5 HeaderFiles ...... 17 3.3.6 SourceCodeFiles ...... 18 3.3.7 Backwardcompatibilityofbugfixes ...... 19

1 4 ns-3 core 20 4.1 Simulator,Scheduler,andEvents ...... 21 4.1.1 Simulator...... 21 4.1.2 Scheduler...... 21 4.1.3 Events...... 22 4.2 Timers...... 23 4.3 Time...... 23 4.4 Callbacks ...... 23 4.5 Referencelistimplementation ...... 24 4.6 FileI/Oandsystemtime ...... 24

5 Running ns-3 simulations 25 5.1 Executingsimulations...... 25 5.1.1 main()program...... 25 5.1.2 Scriptinginterface ...... 27

6 Packets 28

7 Tracing and Logging 29 7.1 Requirements ...... 29 7.1.1 traceformatforTraceGraphsupport ...... 30

8 Acknowledgments 32

2 Chapter 1

Functional Overview

This chapter describes the ns-3 simulator from a functional or user’s perspective; i.e., without as much regard to internal implementation.

1.1 Goals

This section describes the broad goals for ns-3.

• ns-3 is a discrete-event networking simulator, written in ++, with an emphasis on layers 2-4 of the OSI stack, including IPv4, IPv6, and future next-generation (non-IP) networks. • ns-3 is oriented towards supporting networking research and education via simulation. • ns-3 is free software developed using a community-oriented, open source development process, under GNU GPLv2 compatible licensing.

1.2 User experience

1.2.1 Installation ns-3 should be buildable from source or binary formats on popular desktop and server platforms, including x86, x86-64, and ppc, and the Linux, OS X (Darwin), Windows (32-bit, build environment TBD), Solaris, and BSD (FreeBSD and others) operating systems.

1.2.2 User interface

• ns-3 should continue to offer text/script-based (non-GUI) configuration. It should be possible to create GUI-based configurators, but such configurators are outside the scope of the ns-3 project. • ns-3 should output trace (including pcap), log, statistics, and animation files

3 – trace and log files should be convertible to the existing nam format, via some external scripting technique, for backward compatibility. • Open issue: should ns-3 be directly integrated with an animator or should the animator just run on output files (post-process)?

1.2.3 Scenario definition

Users use the simulator by first defining a simulation scenario, compiling the scenario if necessary, executing the simulation scenario, and processing the simulation output, either visually through an animator, or through other handling of the output files generated. Often, users of the simulator will directly modify the simulator source code as a starting point.

• ns-3 scenarios can be written in C++ (as a main() function), with selected configuration options exposed as command- line arguments • ns-3 should provide a scripting environment or interface – full backward compatibility with ns-2 scripts is a non-goal – Open issue: Selection of scripting language(s) is TBD. Preferred option presently is python and possibly SWIG bindings. • scenario execution is visible on a console standard output, or written to a log file. • support for some stock topology constructs should be provided

1.2.4 Capabilities

• ns-3 shall provide an emulation capability– ability to source/sink real packets and execute in real-time • ns-3 shall be designed to scale for parallel processor support and distributed simulations, in a manner mostly transparent to users. • ns-3 shall provide interfaces that facilitate the porting of implementation code (user space and kernel TCP/IP stacks)

1.3 Documentation

• Source code APIs shall be documented using Doxygen and available on the web as HTML and Latex-generated PDF. • the various project documents use Latex.

The documentation should be checked out nightly, built into PDF and HTML, and posted on the web. Documentation should be stored in the source code repository in source form, with figures stored in eps or image format. Vector graphics should be drawn in a commonly available vector graphics program such as dia, tgif, or xfig, and the sources stored with the eps.

1.4 Requirements from Collaborators

This project is collaborating with several other networking research projects. This section is used to document the goals and/or requirements that have been suggested from these other projects.

4 1.4.1 VINI

Nick Feamster and George Riley met in September 2006 and agreed that the ns-3 team would strive to support VINI on two basic fronts:

• ns-3 simulations will provide some mechanism to "dump" the topology in a format compatible with VINI’s topology description. VINI would then re-create the same topology on the VINI/Planetlab systems and re-create the same experiment. • The ns-3 design will facilitate "easy" (definition of easy TBD) migration of application code to and from VINI. This is harder, but is consistent with our design goals of "easy" integration of existing code (eg. the XORP OSPF implemen- tation). The issues are still evolving, but we expect that whatever solution we converge on for the general case will be sufficient for the VINI case.

1.4.2 Emulab

1.4.3 Network Simulation Cradle

1.4.4 Large scale simulations

(include Nick Weaver comments here)

5 Chapter 2

Technical Overview

2.1 Basics ns-3 is a user-space program that runs on Unix- and Linux-based systems and on Windows (build process, via Cygwin or via native win32 APIs, is to be determined). It contains support for the following:

• construction of virtual networks (nodes, channels, applications) and supportfor items such as eventschedulers, topology generators, timers, and the like to support discrete-event network simulation focused on Internet-based and possibly other packet network systems. • support for network emulation; the ability for simulator processes to emit and consume real network packets • distributed simulation support; the ability for simulations to be distributed across multiple processors or machines • support for animation of network simulations • support for tracing, logging, and computing statistics on the simulation output ns-3 is written in C++, with a planned Python scripting interface(s) for users.

Contributors are expected to conform to the project coding style, licensing, and copyright requirements (licensing and copy- right requirements described in the project plan document).

6 Node

App ... App Unix-like APIs (sockets, netfilter, pfkey, etc.)

Stack Stack (e.g. (e.g. IPv4) ... IPv6) Packet Net/3-like interface API * unique id (m_uid)

Interface ... Interface * Buffer object

* Tags container object

Channel Channel

Figure 2.1: High-level architecture for ns-3 project.

2.2 High-level architecture ns-3 has a modular implementation containing a core library supporting generic aspects of the simulator (scheduler, events, packets, random number generators, tracing, logging, and statistics, etc.) and a few abstract base classes, just to get the architecture and interfaces defined consistently. ns-3 uses a stack-based architecture (where stack refers to a data stack and not the data structure). The basic idea is to more closely follow how typical Internet-based computers are structured, both to facilitate software reuse (e.g., Network Simulation Cradle by Jansen, porting of application code) and for educational purposes. Comparing with ns2, this architecture probably most resembles that of the CMU Monarch extensions (wireless).

Figure 2.1 provides an overview. Aside from the objects that make up the core simulation environment (such as scheduler, events, etc.), the main objects in the simulator are the Node (which is a composite object), Channel, and Packet. At the highest level, Nodes are interconnected via Channels, and they exchange Packets. The Node object contains a number of objects, including primarily Interface, Stack, and Application objects. Nodes may also have other objects such as Position and Energy objects (to be determined). There are various interfaces defined internally between these objects but there are two important ones. First, applications bind to the kernel object via APIs that closely resemble Unix/Linux APIs (sockets, resolver, netlink, etc.). This allows for easier portability of actual application code, and is useful from an educational standpoint. Second, the interface between the TCP/IP protocol stack and any network interfaces resembles the Net/3 interface, mirroring the modularity found in modern operating systems.

The simulator should allow combinations of different stacks, interfaces, channels, and applications; e.g., we should strive to avoid problems such as exist in ns2 (wireless stack incompatibility with multicast routing).. Different stacks could speak the same protocol (e.g., a Linux stack talking to a BSD stack) or there could be different stacks (e.g., non-TCP-IP). We may be able to port existing ns2 code to a special stack (and make backward compatibility optional if you do not want to compile all of the OTcl). We can focus on a reference TCP/IP stack (for IPv4 and IPv6) as part of the core ns-3 distribution, designed for clarity and extensibility, and allow projects such as the Network Simulation Cradle to create stacks ported from real systems. Not all such stacks need be included in the main distribution and some may need to be external due to licensing compatibility issues anyway.

7 ns-3/

- SConstruct build-dir/ doc/ samples/ src/ utils/ - build.py - build.pyc

where binaries where Doxygen sample scripts benchmarking are built docs are built programs, etc.

common/ core/ simulator/

common simulation- event scheduling simulation objects independent facilities (e.g., Packet) facilities (e.g., callbacks)

Figure 2.2: Code organization for ns-3 project.

2.3 Source code organization

Figure 2.2 provides an overview of the ns-3 source code organization. Section 2.4 below details the build environment and options. The ns-3 library is split across multiple modules:

• core: located in src/core and contains a number of facilities which do not depend on any other module. Some of these facilities are OS-dependent. • simulator: located in src/simulator and contains event scheduling facilities. • common: located in src/common and contains facilities specific to network simulations but shared by pretty much every model of a network component.

A number of files exist in the top-level directory (SConstruct, build.py, and build.pyc) to coordinate the build process.

2.4 Build environment

We are trying the SCons build environment, and will fall back to the GNU build environment (autoconf, automake, libtool, gcc) if SCons doesn’t work out. The default Windows build environment is still under consideration; probably Cygwin or mingw. Microsoft Visual C++ may be considered, but we are working through dll export/import issues.

Other compilers and build environments are outside the scope of the project, but we welcome anyone who wants to try alternatives to document how to use them.

See also the “BUILD” file in the top-level ns-3 directory. Mathieu Lacage organized the ns-3 build process and wrote the configuration files.

8 2.4.1 SCons overview

From the SCons documentation:1

“SCons is a software construction tool (build tool, or make tool) implemented in Python, which uses Python scripts as "configuration files" for software builds. Distinctive features of SCons include: a modular design that lends itself to being embedded in other applications; a global view of all dependencies in the source tree; an improved model for parallel (-j) builds; automatic scanning of files for dependencies; use of MD5 signatures for deciding whether a file is up-to-date; use of traditional file timestamps instead of MD5 signatures available as an option; use of Python functions or objects to build target files; easy user extensibility.”

If you want to build ns-3, you need to install SCons (see http://www.scons.org). SCons takes care of building the whole source tree using your system compiler. SCons 0.91.1 and 0.91.96 have been tested and are known to work on Linux FC5, Mac OS X and MinGW. OS X users may need to install from the DarwinPorts site.

SCons is configured by a “SConstruct” file in the top-level ns-3 directory, as well as a “build.py” file.

To start a build, you can just type ’scons’ which will generate a debug shared build by default, located in the directory ’build-dir/dbg-shared/bin’ and ’build-dir/dbg-shared/lib’.

All builds are built with debugging symbols. Debugging builds enable asserts while optimized builds disable them. On platforms which support it, rpath is used which means that the executable binaries generated link explicitely against the right libraries. This saves you the pain of having to setup environment variables to point to the right libraries.

2.4.2 Options

- verbose: if you have installed SCons 0.91.96 or higher, the default build output is terse. To get a more verbose output, you need to set the ’verbose’ variable to ’y’. Example: scons verbose=y

Compilation flags are set in the build.py file. By default, the following are used:

if cc == 'gcc' and cxx == 'g++': common_flags = ['-g3', '-Wall', '-Werror'] debug_flags = [] opti_flags = ['-O3']

2.4.3 Build targets

- doc: build the doxygen documentation. Example: scons doc

- dbg-shared: a debug build using shared libraries. The files are built in ’build-dir/dbg-shared/’. Example: scons dbg-shared

- dbg-static: a debug build using static libraries The files are built in ’build-dir/dbg-static/’. Example: scons dbg-static

- opt-shared: an optimized build using shared libraries. The files are built in ’build-dir/opt-shared/’. Example: scons opt- shared

- opt-static: an optimized build using static libraries. The files are built in ’build-dir/opt-static/’. Example: scons opt-static

1(http://www.scons.org, Copyright 2001, 2002 by Steven Knight

9 - dbg: an alias for dbg-shared Example: scons dbg

- opt: an alias for opt-shared Example: scons opt

- all: alias for dbg-shared, dbg-static, opt-shared and opt-static Example: scons all

- dist: generate a release tarball and zipfile from the source tree. The tarball and zipfile name are generated according to the version number stored in the SConstruct file. Example in SConstruct: ns3 = Ns3 () ns3.name = ’foo’ ns3.version = ’0.0.10’ Example command: scons dist Example output files: foo-0.0.10.tar.gz foo-0.0.10.zip

- distcheck: generate a release tarball and zipfile and attempt to run the ’all’ target for the release tarball. Example: scons distcheck

2.4.4 Build output

Targets end up in the build-dir/ directory. Different build targets end up in different directories (e.g., dbg-shared/). ns-3 build process builds a library foreach modulein the src/ directory, such as libcommon, libcore,and libsimulator. These are found in the lib/ directory in the build directory. Executables are found in the bin/ directory; these executables are linked against the module libraries during the build process.

10 Chapter 3 ns-3 Coding Standard

3.1 Introduction

The purpose of the ns–3 project is to build software which will last many years: making sure that the code is readable and stays so is one of the most important items required to achieve this goal. This document thus outlines guidelines we plan to enforce on each component integrated in ns–3 to ensure uniformity of the codebase which is a first step towards readable code.

3.2 Recommendations

The following recommendations are not strict rules and some of them are conflicting but the point here is to outline the fact that we value more common-sense than strict adherence to the coding style defined in this document.

3.2.1 naming

Types, methods, functions and variable names should be self-descriptive. Avoid using acronyms, expand them, do not hesitate to use long names, Avoid shortcuts such as sz for size. Long names sometimes get in the way of being able to read the code: for (int loopCount = 0; loopCount < max; loopCount++) { // code } loopCount should be renamed to something shorter such as i, j, k, l, m, and n which are widely used names which identify loop counters: for (int i = 0; i < max; i++) { // code

11 }

Similarly, tmp is a common way to denote temporary variables. On the other hand, foo is not an appropriate name: it says nothing about the purpose of the variable.

If you use predicates (that is, functions, variables or methods which return a single boolean value), prefix the name with is or has.

3.2.2 Memory management

As much as possible, try to pass around objects by value and allocate them on the stack. If you need to allocate objects on the heap with new, make sure that the corresponding call to delete happens where the new took place. i.e., avoid passing around pointer ownership. Avoid the use of reference counting and, more generaly, strive to keep the memory-management model simple.

3.2.3 Templates

For now, templates are defined only in the simulator core and are used everywhere else. Try to keep it that way by avoiding defining new templates in model-specific code.

3.3 Standards

3.3.1 General

1. There will be no tab characters in the code. Rather, repeated spaces are used to separate the characters as needed. 2. No line of code will be longer than 80 characters in length, to prevent lines wrapping in the emacs or vi editors. For both of these linux text editing tools, the default is a window that is exactly 80 characters wide, so if none of the lines wrap when editing in emacs or vi this requirement is met. 3. Each C++ statement will be on a separate line. The only exception is when an if, else, for or while statement has a single statement sub–block these can be on the same line. Examples: int i = 0; // Right i = 10; j = 20; // Wrong. Two statements same line Sub1(k); Sub2(k); // Wrong. Two statements same line if (done) break; // Right. If statement with single statement sub-block

4. Each variable declaration will be on a separate line. Examples:

int c, d; // Wrong. c and d defined on same line. int a = 0; int b = 0;// Right. a and b on different lines

12 5. Variables should be declared at the point in the code where they are needed, and should be assigned an initial value at the time of declaration.

int a = 0; // Right, a is assigned in initial value. int b; Wrong, b is not assigned an initial value. int c = 0; int d = Sub1(a, b); c = Sub2(d);// Wrong, c should be declared here, not above

6. Excepting when used in a switch statement, the open and close curly braces ({ and }) are always on a separate line. Examples:

for (int i = 0; i < MAX_COUNT; ++i) { // Right. Open brace on separate line sum += i; prod *= i; } // Right. Close brace on separate line

for (int i = 0; i < 10; ++i) { // Wrong. Open brace on same line sum += i; prod *= i; } // Wrong. Close brace on same line

7. The C++ goto statement is not to be used.

3.3.2 Commenting

In general, comments should be use liberally throughout the program to increase readability. Specifically:

1. C++ style comments using the // delimeter are to be used, rather than C style comments with the /* and */ delimi- eters. 2. Variable declaration should have a short, one or two line comment describing the purpose of the variable, unless it is a local variable whose use is obvious from the context. The short comment should be on the same line as the variable declaration, unless it is too long, in which case it should be on the preceding lines. Example: int averageGrade; // Computed average grade for this project // Note. The above comment likely qualifies as // obvious from context, and could be omitted. // Counts the total number of students completing the project, but // does not include those not turning in the project. int projectCount = 0;

3. Every function should be preceded by a detailed comment block describing what the function does, what the formal parameters are, and what the return value is (if any). 4. Every class declaration should be preceded by a comment block describing what the class is to be used for. 5. Unless obvious from context, each if statement should include a one–line comment on the open curly brace following describing the TRUE condition and the FALSE condition. Example:

13 if (iter == students.end()) { // Student not found, add him students.push_back(thisStudent); } else { // Student exists, modify existing data iter->grade += thisGrade; }

6. Class and function comments should adhere to the Doxygen standard format, for automated extraction by the Doxygen tool. Note from GFR. We need a bit more here, as Doxygen has several possible methods for commenting. I’ll look them over and suggest an approach, for later discussion

3.3.3 Naming Conventions

1. Variable Names. All variables, including global variables, local variables, formal parameters, and member variables in classes will start with a lower case letter, and consist of only alphabetic characters and numeric digits. Capital letters are to be used when appropriate between words in a variable name for increased readability. Variable names should not contain the underscore character. Examples: int i; int nextIndexValue; int sum1; int loopCount10;

2. Class Member and Global Variables. To be able to distinguish local variables fromclass member and global variables, prepend the m_ prefix to class member variables and the g_ prefix to global variables. Examples:

class Foo { private: int m_myPrivateVar; }; static int g_myGlobalVar;

3. Names. All subroutinenames, including global routines and member functions in classes, will start with an upper case letter, and consist of only alphabetic characters and numeric digits (although digits should be rarely needed). As in variable names, upper case letters are to be used between words as needed to increase readability. Examples: int ComputeNextIterator() int Calculate() int TransmitPacket() int Dummy()

4. Defined Constants. All defined constants will be all upper case letters or numeric digits, with the underscore character separating words. Examples: typedef enum { PACKET_RX, PACKET_FIRST_BIT_RX, PACKET_TX} #define NUMBER_ELEMENTS 10 const int LOOP_COUNT = 100

14 5. Defined Types. All user defined types will end start with an upper case letter, consist of upper and lower case letters only, and end in _t. Examples: typedef double Time_t; // Simulation time typedef unsigned long SimulatorUid_t; // Unique ID for each event typedef unsigned long Event_t; // Idenifies events in handler

6. Class Names. Class names will start with an upper case letter, consist of only alphabetic characters, and include capital letters as needed to increase readability. Examples: class DropTailQueue { class Ferrari {

3.3.4 Statement Formatting

1. Indention. The basic indention level for all code is four character positions. In some cases, indention to “one–half” level, is required as described below. 2. Continuation statements. Frequently a single statement is too long to fit within a single 80 column line. In this case, the statement is simply continued on the next one or more lines. Each continuation line must be indented at least one–half indention level, and more as necessary to increase readability. Examples:

longVariableName = (anotherLongName * shorterName) + (loopIndex2 * i) + (k * j); // Correct, indented for neatness

for (LongTypeName_t longLoopIndexName = aLongExpression; longLoopIndexName < MAX_VALUE; longLoopIndexName++) // Wrong, continuations not indented far enough

for (LongTypeName_t longLoopIndexName = aLongExpression; longLoopIndexName < MAX_VALUE; longLoopIndexName++) // Right, indented for readability

3. IF Statements. The open curly brace following an IF statement must be on the following line, indented by one–half indention level. The subsequent lines must indented an additional one–half indention level. IF statements with only one statement in either the TRUE of FALSE sub–blocks may omit the curly braces. The ELSE statement (if present) must be on a line by itself. Examples:

if (someCondition) { // Describe TRUE condition here i = k; k = i + 2; } // Right, curly block indented one-half, statements one-half more

if (someCondition) { // Describe TRUE condition here i = k;

15 k = i + 2; } else // Right, ELSE statement on separate line, same indent as IF { // Describe FALSE condition here i = k * 2; k = i + 4; } // Right, closing curly brace lined up with open brace

if (someCondition) // Describe TRUE condition here i = k; // Right, single line block need not have curly braces

if (someCondition) i = k; // Right, single statement may be on same line

4. FOR Statements. The open brace following a for statement is indented one-half level from the for statement itself. Each statement in the sub–block is indented one–half level from the curly brace. If the sub–block is a single statement, the curly braces can be omitted and the statement indented one level, or optionally appear on the same line as the for statement. Example:

for (int i = 0; i < MAX_COUNT; ++i) { // Curly brace indented one-half level sum += i; // Statements indented another one-half level prod *= i; } // Close brace on same column as open brace

for (int i = 0; i < MAX_COUNT; ++i) Sub1(i); // Right, single statement

5. WHILE Statements. While statements are formatted similarly to IF statements, with curly braces indented one-half level on separate lines, and the inner statements indented another half-level. If the sub–block has only a single line, the curly braces can be omitted, and the statement may appear on the same line as the WHILE statement. Examples:

while (someCondition) { // Right, open brace indented one-half level i = k; // Right, statements indented one-half level from open brace k = i + 2; } // Right, close brace lines up with open brace

while (someCondition) i = i + 2; // Right, single stmt on same line

6. Infinite Loops. Any loop intended to be infinite (of course with a break statement somewhere) should be of the form: while(true) // Loop until sentinel found ...code here

7. SWITCH Statements. The open curly brace for a switch statement will be on the same line as the switch statement itself. Each case statement following is indented two columns from the switch statement. Each statement in the case block is indented two column from the case statement. The closing curly brace is on a separate line by itself, indented two columns from the switch statement. Example:

16 switch(someCondition) { Right, open brace on same line as switch case 0 : // Right, case indented two columns from switch i = k; // Right, statements indented two columns from case k = i + 2; break; case 1 : // Right, case indented two columns from switch i = k + 2; // Right, statements indented two columns from case k = i + 4; break; } // Right, close brace lines up with case statements

8. Functions. Since C and C++ do not allow nested functions, all functions start with no indentation at column 0. The open curly brace is on a line by itself immediately following the function header and formal parameters, also in column 0. Any local variable declarations immediately following the open curly brace also start at column 0. One blank line follows the initial local variable declarations (if any). The statements in the function body are indented one-half level from the curly brace. Any variable declarations after the start of the statements are indented at the same level as the preceding statement. The closing brace is at column 0. Example:

void Function1(int arg1, double arg2) { // Right, curly brace at column 0 int local1 = 0; // Right, local variable at column 0 int local2;

local2 = local1 + arg1 + arg2; // Right, indented two columns int local3; // Right, variable at same level local3 = Function2(local2); if (someCondition) { local3 = 0; local2 = local1; int local4 = local1 + 1; // Right, variable at same level Function3(local4); } } // Right, close brace at column 0

9. Expressions. Spaces should be used liberally in expressions to increase readability. One space before and after each operator, excepting the increment and decrement operators, leads to easy–to–read expressions. Continued expressions should be indented as far as needed for neatness and readability. Examples: i = k * 2 + 3 / var1++; // Right, spacing separating terms

i = k*2+2/var1++; // Wrong, crowded together and hard to read

someLongVariableName = anotherLongVariableName * shorterName + anotherName; // Right, indented to line up

3.3.5 Header Files

1. All header files will have a file name ending with .h.

17 2. All header files should have a one line comment describing the purpose of the header, and comments identifying the author and the (approximate) date the file was created. Example: // ns3 Network Simulator - TCP Base Class Declaration // George F. Riley. [email protected]. // Georgia Tech, Fall 2006

3. All header files should have an “include guard” to prevent accidental inclusion of the file multiple times in a single compilation unit. The include guard should be named after the file name. If the file name is foo-bar.h, then the include guard should be named FOO_BAR_H Example: #ifndef FOO_BAR_H #define FOO_BAR_H

// (Contents of foo-bar.h here #endif

4. Header files should avoid including other files whenever possible. This can often be avoided with judicious use of the class ClassName; forward declaration. Example: // excerpt from application.h class L4Protocol;

class Application { .... AddL4Proto(const L4Protocol&); .... L4Protocol* l4Proto; }; In the above example, the use of the forward declaration for L4Protocol obviates the need to include l4proto.h in the application header file.

3.3.6 Source Code Files

1. All souce code files will have a file name ending with .cc. 2. All source code files should have a one line comment describing the purpose of the code, and comments identifying the author and the (approximate) date the file was created. Example: // ns3 Network Simulator - TCP Base Class Implementation // George F. Riley. [email protected]. // Georgia Tech, Fall 2006

3. All #include directives should be grouped with system files listed first (eg. #include ), followed by ns–3 defined files (eg. #include "tcp.h"). Within a group, the includes should be sorted in alphabetical order. Example: #include #include #include

18 #include "application.h" #include "dumbbell.h" #include "simulator.h" #include "tcp.h.h"

3.3.7 Backward compatibility of bug fixes

In the past, ns-2 developers commonly inserted knobs (flags) for enabling previous model behavior, even if it was buggy. Going forward, we recommend:

• fix bugs in new releases without adding a new knob to disable the bug fix • document behavior-changing bugfixes (by definition, they are all behavior-changes) • when a user asks for the ability to reproduce a given simulation result obtained with an older release, answer that the ony way to obtain reproduceable results is to use the *exact* same version all the time • be willing to tweak this policy for certain important bug fixes which might be considered really parameters to the model or different versions of the same model. For example, classic TCP bugs.

19 Chapter 4 ns-3 core

This chapter discusses the design and implementation of core elements in ns-3. These items are built in two modules (core and simulator) with no other dependencies on the simulation code. Figure 4.1 illustrates the portion of the code described in this chapter.

ns-3/

- SConstruct build-dir/ doc/ samples/ src/ utils/ - build.py - build.pyc

where binaries where Doxygen sample scripts benchmarking are built docs are built programs, etc.

common/ core/ simulator/

common simulation- event scheduling simulation objects independent facilities (e.g., Packet) facilities (e.g., Documentation callbacks) of simulator core

Figure 4.1: Source code (within dashed oval) described in this chapter.

The items described in this chapter include:

• classes Simulator, Scheduler, and Event (and related classes) • representation of simulation time • facility for defining callbacks • reference list implementation • system-dependent handling of file I/O and system time

20 4.1 Simulator, Scheduler, and Events

This section is concerned with the general structure of the simulator for coordinating the execution of simulation events.

• a base Simulator class • an Event class and some facility for handling events • a base Scheduler class designed to hold Events • some facility for callbacks • support for timers

4.1.1 Simulator

At its most basic, a Simulator object provides a public interface that allows objects to insert Events into a Scheduler, and keeps track of simulation time elapsed.

The Simulator class provides a single static Simulator object. The most common operations are to call schedule() to add events to the scheduler. The below program snippet gives an example: int main (int argc, char *argv[]) { MyModel model;

Simulator::schedule (Time::absS (10.0), &random_function, &model);

Simulator::run ();

Simulator::destroy (); }

4.1.2 Scheduler

The Scheduler is an object that dynamically stores Events in some type of ordered data structure. There are various structures possible (linked list, heap, map, etc.) to hold the events; depending on the type and number of Event manipulations required in a simulation, one type of underlying scheduler may perform better than another.

The simulator supports runtime replacement of the underlying event scheduler through the base class Scheduler.

The three provided schedulers are:

• Linked List (insert: O(n), remove: O(1) • Binary Heap (insert: O(log(n)), remove: O(log(n))) • Std Map (insert: O(log(n)), remove: O(log(n)))

21 plus the simulator allows a user to insert his or her own scheduler.

The scheduling order of events scheduled to expire at the same time is specified to be that of the insertion time. i.e.: the events inserted first are scheduled first. This order holds whatever the scheduling algorithm chosen: it is implemented by using an event sequence number, incremented for each insert.

4.1.3 Events

An Event is an object that tells the simulator to do something at a specific time. There are a few related concepts to discuss here: events, timers, and callbacks

The ns2 way of doing things: In ns2, Events are consumed by a subclass derived from class Handler. This leads to the convention that class Handler, which provides pure virtual handle() method that is called when the event executes, is subclassed when needed (and such classes are declared friend to classes that use the Handlers), and the pure virtual handle() method is tailored by the specific Handlers to consume specific events. Class Event is a wrapper around Handler, defined as a node in a linked list.

The yans event design: A yans event is a wrapper around a C++ method or function. A yans Event is a wrapper around a C++ method or function that also holds the value of the arguments passed to the method/function. The method consuming the event can be any method in scope (possibly with arguments). The Event subclass is simple, providing a pure virtual notify() that consumes the event (you need to invoke delete on an Event explicitly after calling notify).

At this point, the yans Event and ns-2 Handle provide similar functionality: the departure from ns-2 way of doing things is that a set of templates are used to automatically generate the code for the subclasses of the Event/Handler class. These automatically-generated classes are ’forwarder’ classes which ’forward’ the event notification to arbitrary functions or class methods with an arbitrary number of arguments. These templates implement a version of the Command design pattern. In terms of Boost, a yans Event is a fully-bound functor.

These templates export a single overloaded function to the user: make_event (method, arguments) which is a constructs the right type of Event from a method or function pointer and the arguments. The Event object is then passed down to the simulator’s schedule methods.

The ns-3 event design: ns-3 has adopted the technique used in yans with a small change: the make_event method has been integrated in the in the main Simulator::schedule method which saves users quite a bit of typing. ns-3 thus does:

Simulator::schedule (time, &method, arguments); while yans does:

Simulator::schedule (time, make_event (&method, arguments));

The ns-3 version also makes the memory management of events simpler and allows us to avoid using smart pointer and refcounting to manage the memory associated to events.

Cancelling an event There are two basic options to cancel an event:

22 • set the cancel bit on an event: when the event expires, if its cancel bit is set, we do not run the event’s notify method. This operation usually has O(1) algorithmic complexity • remove the event from the event list. This operation usually has at least O(log(n)) complexity.

Although both methods have the same semantics, they have different complexity behaviors. Currently, ns-3 supports both through the following methods:

• Simulator::cancel (eventId: set the cancel bit • Simulator::remove (eventId): remove from event list

For convenience, the EventId class also exports a cancel method.

4.2 Timers

In ns2, timers (class TimerHandler) are derived from Handlers. At a high-level, they wrap Events that are inserted into the scheduler, providing methods like cancel(), sched() and resched().

In ns-3, timers are simply Events; the method to call upon expiry is stored in the Event as a function pointer, and the Event may be cancelled by either explicit removal of the Event from the scheduler queue, or by setting a cancel bit.

4.3 Time

Simulation time may either be represented as a floating point or integer number internally. Historically, the handling of events in ns2 on different platforms led to different event ordering due to floating point arithmetic differences (rounding) on different platforms. In ns-3, time is maintained internally as nanosecond integers (stored internally in a uint_64). ns-3 provides a Time class for safer usage of simulation time. The class provides a number of static member functions allowing users to create a Time object representing time according to different units; examples include:

• Time::absS(double s) Return Time object corresponding to an absolute time of s seconds into the simulation. • Time::relS(double s) Return Time object corresponding to a relative time of s seconds beyond the current simulation time. • Time::now(void) Return Time object corresponding to the current simulation time.

4.4 Callbacks

The callback API in ns-3 is designed to minimize the overall coupling between various pieces of of the simulator by making each module depend on the callback API itself rather than depend on other modules. It acts as a sort of third-party to which work is delegated and which forwards this work to the proper target module. This callback API, being based on C++ templates, is type-safe; that is, it performs static type checks to enforce proper signature compatibility between callers and callees. The API is minimal, providing only two services:

23 • callback type declaration: a way to declare a type of callback with a given signature, and, • callback instantiation: a way to instantiate a template-generated forwarding callback which can forward any calls to another C++ class member method or C++ function.

The implementation is based on use of templates to implement the Functor Design Pattern. It is used to declare the type of a Callback:

• the first non-optional template argument represents the return type of the callback. • the second optional template argument represents the type of the first argument to the callback. • the third optional template argument represents the type of the second argument to the callback. • the fourth optional template argument represents the type of the third argument to the callback. • the fifth optional template argument represents the type of the fourth argument to the callback. • the sixth optional template argument represents the type of the fifth argument to the callback.

Callback instances are built with the makeCallback template functions. Callback instances have plain old data (POD) seman- tics: the memory they allocate is managed automatically, without user intervention which allows one to pass around Callback instances by value.

Walk-through example here

Example

4.5 Reference list implementation

4.6 File I/O and system time

24 Chapter 5

Running ns-3 simulations

The previous chapter detailed some core implementations of various aspects of the simulator. This chapter builds on the previous by describing the elements of writing rudimentary programs to use the core. Subsequent chapters detail the design and implementation of other features (packets, channels, nodes, etc.) necessary to run useful network simulations.

This chapter walks through a simple example script, found in the location samples/main-simulator.cc.

5.1 Executing simulations

In ns2, simulation scripts are written in OTcl. In ns-3, simulation scripts are written in C++, with support for extensions that allow simulation scripts to be written in Python scripting language. These Python bindings have yet to be written.

5.1.1 main() program

Simulations are executed as C++ programs that link compiled object code and libraries, instantiate and create bindings be- tween objects, and create a simulator object that controls the running of the objects in simulation time.

A basic skeleton of what this will look like is below:

/* -*- Mode:C++; c-basic-offset:4; tab-width:4; indent-tabs-mode:f -*- */ #include "ns3/simulator.h" #include "ns3/time.h" #include using namespace ns3;

The first few lines include an emacs mode line, include files, and a using directive to pull in the ns3 namespace. This line is important because all core ns-3 code is found in the ns3 namespace. Users can define and include other namespaces as well.

Next, let’s create a simple dummy class. This class has two member functions; a start() function that schedules an event at ten seconds after it is invoked, and a function to deal with the event, using the event scheduling technique described in the previous chapter.

25 class MyModel { public: void start (void); private: void dealWithEvent (double eventValue); }; void MyModel::start (void) { Simulator::schedule (Time::relS (10.0), &MyModel::dealWithEvent, this, Simulator::now ().s ()); } void MyModel::dealWithEvent (double value) { std::cout << "Member method received event at " << Simulator::now ().s () <<

"s started at " << value << "s" << std::endl; }

Now, for good measure, let’s show how one schedules a random function to execute at some time in the future. This function, upon being called, will start the MyModel object. static void random_function (MyModel *model) { std::cout << "random function received event at " << Simulator::now ().s () << "s" << std::endl; model->start (); }

Finally, the main() program is pretty simple. int main (int argc, char *argv[]) { MyModel model;

Simulator::schedule (Time::absS (10.0), &random_function, &model);

Simulator::run ();

Simulator::destroy (); } and it produces the following output: random function received event at 10s Member method received event at 20s started at 10s

26 In the next chapters, we document how more useful network simulation objects are designed and implemented, before later walking through a more sophisticated simulation program example.

5.1.2 Scripting interface

Open issue: How to support scripting.

It seems generally accepted that ns-3 should have a scripting interface, not based on OTcl, but opinions differ on what is the right alternative. There have been a few options suggested:

• SWIG (http://www.swig.org) • boost.python • Ruby • reuse/generalize the GIMP PDB (http://www.advogato.org/article/550.html).

It may be likely that we get some novel contributions from the community on how to do scripting in multiple ways.

Recent discussions with the M5 simulation project suggest that Python is a good choice, with possibly support for SWIG bindings. Mathieu Lacage plans to add some hooks for Python scripting.

27 Chapter 6

Packets

Packets (messages) are fundamental objects in the simulator and their design is important from a performance and resource management perspective. There are various ways to design the simulation packet, and tradeoffs among the different ap- proaches. In particular, there is a tension between ease-of-use, performance, and safe interface design.

There are a few requirements on this object design:

• Creation, management, and deletion of this object should be as simple as possible, while avoiding the chance for memory leaks and/or heap corruption; • Packets should support serialization and deserialization so that network emulation is supported. • It should be natural for packets to carry actual application data, or if there is only an emulated application and there is no need to carry dummy bytes, smaller packets could be used with just the headers and the data size, but not bytes, conveyed in the simulated packet. • Packets should provide an interface to emulate BSD-like operations on mbufs. • Additional side-information should be supported, such as a tag for cross-layer information.

(Tom to fill in more details here)

28 Chapter 7

Tracing and Logging

ns-3 plans to improve the data collection and output processing capabilities over that of ns2. In ns2, the basic Data collection could be improved over what is in ns2. The basic capabilities are for traces (out.tr, out.nam) and monitors. The tracing cannot be controlled at a fine granularity (e.g. trace only the foreground traffic). Monitors are primarily associated with queues.

Tracing and logging refer to simulation output that shows (with a timestamp) that a particular event occurred. Tracing generally refers to packet events, while logging refers to e.g., process log events, although the two are somewhat related.

7.1 Requirements

Some requirements on this have been expressed:

• Ability to generate pcap formatted tracefiles, for use in analyzers such as Ethereal. • Ability to take (parse) pcap files as input to traffic generators (under discussion on ns mailing list). • if backward compatibility for nam or ns-2 traces is desired, ability to post-process the ns-3 trace format to generate .tr and .nam files. yans has added support already for writing pcap files.

In yans, every object which wants to trace something creates one of:

• packet logger (can log packets) • stream tracer (can log arbitrary strings and data to a stdc++ ostream with operator«) • variable tracer (logs every change of the value of the variable

Every object also needs to report about these trace objects in a TraceContainer whenever the user asks for it. Then, the user connects callbacks to any of these trace objects, each of which is identified by a unique string. We provide helper callbacks which automatically log the trace data to files. The pcap output support is implemented as a packet writer that can be connected to any number of packet loggers. The granularity of this approach is very small, which allows the user to:

29 • choose only the trace sources it is interested in (for example, ipv4 drops from node A, mac drops from node B and 802.11 rx from node C). • connect arbitrary filtering code to these trace events • connect default dumping code to these trace events

We are considering moving the yans framework for tracing to ns-3.

Open issue: Whether trace files have a unique object ID embedded in the trace line itself (e.g. a single trace file for the simulation), or whether each object generates its own trace file and the object name is part of the trace file name.

7.1.1 trace format for TraceGraph support

Jaroslaw Malek contributed the TraceGraph1 utility to ns2 and has contributed the following ideas for revising the tracing:

For Matlab processing only, something like Tracegraph format is easy to load. For stream processing like I used in the Trace converter, new trace format is very good (much easier to parse than old-wireless one). For the fastest processing, the new format would have to contain the same tags (fields, number of columns) with different/empty values in each line because checking a line if it’s correct takes some time (like old wireless format is very messy, new trace format also is not consistent but much better) and with the same tags there would be no need to check lines.

There should be a few formats:

1. binary - suitable for parsing using streams like in C, java, etc. there should be the same number of fields (also the same field size for each event would be good), adding a new field with a new format version would be easy to support in the parser 2. text (lines, like new trace format) - suitable for parsing using awk, perl, etc. 3. XML - object format (1 event have n objects, 1 object have n fields, n events in the file) slower but easier to read, convert and analyse using many tools, e.g. warehouse and statistical applications 4. multi-segment for multi-threaded processing with no need to load the whole file into memory, i.e. statistics could be calculated on the fly during parsing for each segment and finally gathered in one package.

There should be the same format for wired, wireless and other events (not like wired-cum-wireless which is too complicated for many people as I can see on Trace graph and ns-2 groups). As for above formats details I haven’t thought about them yet. :) New format would better require to rewrite Trace graph to obtain correct analysis results (actually it could be converted to Trace graph format but calculations could be wrong - it depends on the new format) so it could inspire somebody (maybe me) to write a new application. If there was a free analysis tool supplied with ns-2 everybody would be happy and there would be no need to create scripts and programs to analyse trace files.

A few more things have come to my mind. Each generated packet should have an unique ID (no distinction between e.g. wired or wireless event, ID should be unique for the whole trace file), so tracing it (e.g. for delays calculations) would be easy. There should be packet type, e.g. wired (and subcategories), wireless (and subcategories), etc., for each event. Correct sequence numbers would also be good for RTT calculations (it should be easy to find out which ACK packet corresponds to a sent packet). The new format should be easy not only to parse but also easy to calculate statistics, so it’s good to think how easy would be to calculate them (and if the results would be 100% correct) in a fastest way with the new format (and maybe put some example algorithms or just explanations in ns-3 documentation). Because I’ve made Tracegraph with MATLAB I represent trace file as a matrix (the same number of columns) and all the calulations are simpler to implement in that way.

1http://www.tracegraph.com

30 There should be no IP addresses in the ns-3 trace format. There should be only node numbers and a mapping file (for example .tr.ip extension), e.g. a line could be like: node_number IP_address.

Note: Global unique IDs break assumptions about emulation and may not be supported. Having no IP addresses may also cause emulation and real-world code integration problems.

31 Chapter 8

Acknowledgments

The following people have contributed to this document, via direct text contribution, comments, or mailing list posts:

• Kevin Fall • Tom Henderson • Mathieu Lacage • Jaroslaw Malek • Steve McCanne • George Riley

Mathieu Lacage and George Riley have been instrumental in setting the early design framework for ns-3.

32