Manual: Nodes and Packet Forwarding

From nsnam
Revision as of 08:07, 28 November 2008 by Lachlan (Talk | contribs) (Tidying)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Previous chapter: "The Class Simulator" | index | Next chapter: "Links: Simple Links"

This chapter describes one aspect of creating a topology in ns, i.e., creating the nodes. In the next chapter, we will describe second aspect of creating the topology, i.e., connecting the nodes to form links.

Recall that each simulation requires a single instance of the class Simulator to control and operate that simulation. The class provides instance procedures to create and manage the topology, and internally stores references to each element of the topology. We begin by describing the procedures in the class Simulator. We then describe the instance procedures in the class Node to access and operate on individual nodes. We conclude with detailed descriptions of the Classifier from which the more complex node objects are formed.

The procedures and functions described in this chapter can be found in ~ns/tcl/lib/ns-lib.tcl, ~ns/tcl/lib/ns-node.tcl, ~ns/tcl/lib/ns-rtmodule.tcl, ~ns/rtmodule.{cc,h}, ~ns/classifier.{cc, h}, ~ns/, ~ns/, ~ns/, and, ~ns/

Node Basics

The basic primitive for creating a node is

set ns [new Simulator]
$ns node

The instance procedure node constructs a node out of more simple classifier objects. The Node itself is a standalone class in OTcl. However, most of the components of the node are themselves TclObjects. The typical structure of a (unicast) node is as shown in Figure 5.1. This simple structure consists of two TclObjects: an address classifer (classifer_) and a port classifier (dmux_). The function of these classifiers is to distribute incoming packets to the correct agent or outgoing link.

All nodes contain at least the following components:

  • an address or id_, monotonically increasing by 1 (from initial value 0) across the simulation namespace as nodes are created,
  • a list of neighbors (neighbor_),
  • a list of agents (agent_),
  • a node type identifier (nodetype_), and
  • a routing module

By default, nodes in ns are constructed for unicast simulations. In order to enable multicast simulation, the simulation should be created with an option “-multicast on”, e.g.:

set ns [new Simulator -multicast on]

The internal structure of a typical multicast node is shown in Figure 5.2.

When a simulation uses multicast routing, the highest bit of the address indicates whether the particular address is a multicast address or an unicast address. If the bit is 0, the address represents a unicast address, else the address represents a multicast address.

Node Methods: Configuring the Node

Procedures to configure an individual node can be classified into:

  • Control functions
  • Address and Port number management, unicast routing functions
  • Agent management
  • Adding neighbors

We describe each of the functions in the following paragraphs.

Control functions

  1. $node entry returns the entry point for a node. This is the first element which will handle packets arriving at that node. The Node instance variable, entry_, stores the reference this element. For unicast nodes, this is the address classifier that looks at the higher bits of the destination address. The instance variable, classifier_ contains the reference to this classifier. However, for multicast nodes, the entry point is the switch_ which looks at the first bit to decide whether it should forward the packet to the unicast classifier, or the multicast classifier as appropriate.
  2. $node reset will reset all agents at the node.

Address and Port number management The procedure $node id returns the node number of the node. This number is automatically incremented and assigned to each node at creation by the class Simulator method, $ns node. The class Simulator also stores an instance variable array (i.e., an instance variable of a class that is also an array variable), Node_, indexed by the node id, and contains a reference to the node with that id.

The procedure $node agent <port> returns the handle of the agent at the specified port. If no agent at the specified port number is available, the procedure returns the null string.

The procedure alloc-port returns the next available port number. It uses an instance variable, np_, to track the next unallocated port number.

The procedures, add-route and add-routes, are used by unicast routing to add routes to populate the classifier_ The usage syntax is $node add-route <destination id> <TclObject>. TclObject is the entry of dmux_, the port demultiplexer at the node, if the destination id is the same as this node’s id, it is often the head of a link to send packets for that destination to, but could also be the the entry for other classifiers or types of classifiers.

$node add-routes <destination id> <TclObjects> is used to add multiple routes to the same destination that must be used simultaneously in round robin manner to spread the bandwidth used to reach that destination across all links equally. It is used only if the instance variable multiPath_ is set to 1, and detailed dynamic routing strategies are in effect, and requires the use of a multiPath classifier. We describe the implementation of the multiPath classifier later in this chapter; however, we defer the discussion of multipath routing to the chapter on unicast routing.

The dual of add-routes{} is delete-routes{}. It takes the id, a list of TclObjects, and a reference to the simulator's nullagent. It removes the TclObjects in the list from the installed routes in the multipath classifier. If the route entry in the classifier does not point to a multipath classifier, the routine simply clears the entry from classifier_, and installs the nullagent in its place.

Detailed dynamic routing also uses two additional methods: the instance procedure init-routing{} sets the instance variable multiPath_ to be equal to the class variable of the same name. It also adds a reference to the route controller object at that node in the instance variable, rtObject_. The procedure rtObject?{} returns the handle for the route object at the node.

Finally, the procedure intf-changed{} is invoked by the network dynamics code if a link incident on the node changes state. Additional details on how this procedure is used are discussed later in the chapter on network dynamics.

Agent management Given an <agent>, the procedure attach{} will add the agent to its list of agents_, assign a port number the agent and set its source address, set the target of the agent to be its (i.e., the node's) entry{}, and add a pointer to the port demultiplexer at the node (dmux_) to the agent at the corresponding slot in the dmux_ classifier.

Conversely, detach{} will remove the agent from agents_, and point the agent's target, and the entry in the node dmux_ to nullagent.

Tracking Neighbors Each node keeps a list of its adjacent neighbors in its instance variable, neighbor_. The procedure add-neighbor{} adds a neighbor to the list. The procedure neighbors{} returns this list.

Node Configuration Interface

NOTE: This API, especially its internal implementation which is messy at this point, is still a moving target. It may undergo significant changes in the near future. However, we will do our best to maintain the same interface as described in this chapter. In addition, this API currently does not cover all existing nodes in the old format, namely, nodes built using inheritance, and parts of mobile IP. It is principally oriented towards wireless and satellite simulation. [Sep 15, 2000; updated June 2001].

Simulator::node-config{} accommodates flexible and modular construction of different node definitions within the same base Node class. For instance, to create a mobile node capable of wireless communication, one no longer needs a specialized node creation command, e.g., dsdv-create-mobile-node{}; instead, one changes default configuration parameters, such as

$ns node-config -adhocRouting dsdv

before actually creating the node with the command: $ns node. Together with routing modules, this allows one to combine "arbitrary" routing functionalities within a single node without resorting to multiple inheritance and other fancy object gimmicks. We will describe this in more detail in the section on routing module and classifier organization. The functions and procedures relevant to the new node APIs may be found in ~ns/tcl/lib/ns-node.tcl.

The node configuration interface consists of two parts. The first part deals with node configuration, while the second part actually creates nodes of the specified type. We have already seen the latter in the section on node basics; in this section we will describe the configuration part.

Node configuration essentially consists of defining the different node characteristics before creating them. They may consist of the type of addressing structure used in the simulation, defining the network components for mobilenodes, turning on or off the trace options at Agent/Router/MAC levels, selecting the type of adhoc routing protocol for wireless nodes or defining their energy model.

As an example, node-configuration for a wireless, mobile node that runs AODV as its adhoc routing protocol in a hierarchical topology would be as shown below. We decide to turn tracing on at the agent and router level only. Also we assume a topology has been instantiated with "set topo [new Topography]". The node-config command would look like the following:

$ns_ node-config -addressType hierarchical \
                 -adhocRouting AODV \
                 -llType LL \
                 -macType Mac/802_11 \
                 -ifqType Queue/DropTail/PriQueue \
                 -ifqLen 50 \
                 -antType Antenna/OmniAntenna \
                 -propType Propagation/TwoRayGround \
                 -phyType Phy/WirelessPhy \
                 -topologyInstance $topo \
                 -channel Channel/WirelessChannel \
                 -agentTrace ON \
                 -routerTrace ON \
                 -macTrace OFF \
                 -movementTrace OFF

Note that the config command can be broken down into separate lines like

$ns_ node-config -addressingType hier
$ns_ node-config -macTrace ON

The options that need to be changed may only be called. For example after configuring for AODV mobilenodes as shown above (and after creating AODV mobilenodes), we may configure for AODV base-station nodes in the following way:

$ns_ node-config -wiredRouting ON

While all other features for base-station nodes and mobilenodes are same, the base-station nodes are capable of wired routing, while mobilenodes are not. In this way we can change node-configuration only when it is required.

All node instances created after a given node-configuration command will have the same property unless a part or all of the node-config command is executed with different parameter values. And all parameter values remain unchanged unless they are explicitly changed. So after creation of the AODV base-station and mobilenodes, if we want to create simple nodes, we will use the following node-configuration command:

$ns_ node-config -reset

This will set all parameter values to their default setting which basically defines configuration of a simple node.

Currently, this type of node configuration is oriented towards wireless and satellite nodes. Table 5.1 lists the available options for these kinds of nodes. The example scripts ~ns/tcl/ex/simple-wireless.tcl and ~ns/tcl/ex/sat-mixed.tcl provide usage examples.

Table 5.1 Available options for node configuration (see tcl/lib/ns-lib.tcl).
option available values default
addressType flat, hierarchical flat
both satellite- and wireless-oriented
wiredRouting ON, OFF OFF
llType LL, LL/Sat ""
macType Mac/802_11, Mac/Csma/Ca, Mac/Sat, Mac/Sat/UnslottedAloha, Mac/Tdma ""
ifqType Queue/DropTail, Queue/DropTail/PriQueue ""
phyType Phy/WirelessPhy, Phy/Sat ""
propType Propagation/TwoRayGround, Propagation/Shadowing ""
propInstance Propagation/TwoRayGround, Propagation/Shadowing ""
antType Antenna/OmniAntenna ""
channel Channel/WirelessChannel, Channel/Sat ""
topoInstance <topology file> ""
mobileIP ON, OFF OFF
energyModel EnergyModel ""
initialEnergy <value in Joules> ""
rxPower <value in W> ""
txPower <value in W> ""
idlePower <value in W> ""
agentTrace ON, OFF OFF
routerTrace ON, OFF OFF
macTrace ON, OFF OFF
movementTrace ON, OFF OFF
errProc UniformErrorProc ""
FECProc  ?  ?
toraDebug ON, OFF OFF
satNodeType polar, geo, terminal, geo-repeater ""
downlinkBW <bandwidth value, e.g. "2Mb"> ""

The Classifier

The function of a node when it receives a packet is to examine the packet's fields, usually its destination address, and on occasion, its source address. It should then map the values to an outgoing interface object that is the next downstream recipient of this packet.

In ns, this task is performed by a simple classifier object. Multiple classifier objects, each looking at a specific portion of the packet, forward the packet through the node. A node in ns uses many different types of classifiers for different purposes. This section describes some of the more common, or simpler, classifier objects in ns.

We begin with a description of the base class in this section. The next subsections describe the address classifier, the multicast classifier, the multipath classifier, the hash classifier, and finally, the replicator.

A classifier provides a way to match a packet against some logical criteria and retrieve a reference to another simulation object based on the match results. Each classifier contains a table of simulation objects indexed by slot number. The job of a classifier is to determine the slot number associated with a received packet and forward that packet to the object referenced by that particular slot. The C++ class Classifier (defined in ~ns/classifier.h) provides a base class from which other classifiers are derived.

class Classifier : public NsObject {
   void recv(Packet*, Handler* h = 0);
   void install(int slot, NsObject*);
   void clear(int slot);
   virtual int command(int argc, const char*const* argv);
   virtual int classify(Packet *const) = 0;
   void alloc(int);
   NsObject** slot_;     /* table that maps slot number to a NsObject */
   int nslot_;
   int maxslot_;

The classify() method is pure virtual, indicating the class Classifier is to be used only as a base class. The alloc() method dynamically allocates enough space in the table to hold the specified number of slots. The install() and clear() methods add or remove objects from the table. The recv() method and the OTcl interface are implemented as follows in ~ns/

 * objects only ever see "packet" events, which come either
 * from an incoming link or a local agent (i.e., packet source).
void Classifier::recv(Packet* p, Handler*)
   NsObject* node;
   int cl = classify(p);
   if (cl < 0 || cl >= nslot_ || (node = slot_[cl]) == 0) {
       Tcl::instance().evalf("%s no-slot %d", name(), cl);
int Classifier::command(int argc, const char*const* argv)
   Tcl& tcl = Tcl::instance();
   if (argc == 3) {
        * $classifier clear $slot
       if (strcmp(argv[1], "clear") == 0) {
           int slot = atoi(argv[2]);
           return (TCL_OK);
        * $classifier installNext $node
       if (strcmp(argv[1], "installNext") == 0) {
           int slot = maxslot_ + 1;
           NsObject* node = (NsObject*)TclObject::lookup(argv[2]);
           install(slot, node);
           tcl.resultf("%u", slot);
           return TCL_OK;
       if (strcmp(argv[1], "slot") == 0) {
           int slot = atoi(argv[2]);
           if ((slot >= 0) || (slot < nslot_)) {
               tcl.resultf("%s", slot_[slot]->name());
               return TCL_OK;
           tcl.resultf("Classifier: no object at slot %d", slot);
           return (TCL_ERROR);
   } else if (argc == 4) {
        * $classifier install $slot $node
       if (strcmp(argv[1], "install") == 0) {
           int slot = atoi(argv[2]);
           NsObject* node = (NsObject*)TclObject::lookup(argv[3]);
           install(slot, node);
           return (TCL_OK);
   return (NsObject::command(argc, argv));

When a classifier recv()’s a packet, it hands it to the classify() method. This is defined differently in each type of classifier derived from the base class. The usual format is for the classify() method to determine and return a slot index into the table of slots. If the index is valid, and points to a valid TclObject, the classifier will hand the packet to that object using that object’s recv() method. If the index is not valid, the classifier will invoke the instance procedure no-slot{} to attempt to populate the table correctly. However, in the base class Classifier::no-slot{} prints an error message and terminates execution.

The command() method provides the following instproc-likes to the interpreter:

  • clear{<slot>} clears the entry in a particular slot.
  • installNext{<object>} installs the object in the next available slot, and returns the slot number.
Note that this instproc-like is overloaded by an instance procedure of the same name that stores a reference to the object stored. This then helps quick query of the objects installed in the classifier from OTcl.
  • slot{<index>} returns the object stored in the specified slot.
  • install{<index>, <object>} installs the specified <object> at the slot <index>.
Note that this instproc-like too is overloaded by an instance procedure of the same name that stores a reference to the object stored. This is also to quickly query of the objects installed in the classifier from OTcl.

Address Classifiers

An address classifier is used in supporting unicast packet forwarding. It applies a bitwise shift and mask operation to a packet’s destination address to produce a slot number. The slot number is returned from the classify() method. The class AddressClassifier (defined in ~ns/ is defined as follows:

class AddressClassifier : public Classifier {
   AddressClassifier() : mask_(~0), shift_(0) {
   bind("mask_", (int*)&mask_);
   bind("shift_", &shift_);
   int classify(Packet *const p) {
       IPHeader *h = IPHeader::access(p->bits());
       return ((h->dst() >> shift_) & mask_);
   nsaddr_t mask_;
   int shift_;

The class imposes no direct semantic meaning on a packet's destination address field. Rather, it returns some number of bits from the packet’s dst_ field as the slot number used in the Classifier::recv() method. The mask_ and shift_ values are set through OTcl.

Multicast Classifiers

The multicast classifier classifies packets according to both source and destination (group) addresses. It maintains a (chained hash) table mapping source/group pairs to slot numbers. When a packet arrives containing a source/group unknown to the classifier, it invokes an Otcl procedure Node::new-group{} to add an entry to its table. This OTcl procedure may use the method set-hash to add new (source, group, slot) 3-tuples to the classifier's table. The multicast classifier is defined in ~ns/ as follows:

static class MCastClassifierClass : public TclClass {
   MCastClassifierClass() : TclClass("Classifier/Multicast") {}
   TclObject* create(int argc, const char*const* argv) {
       return (new MCastClassifier());
} class_mcast_classifier;
class MCastClassifier : public Classifier {
   int command(int argc, const char*const* argv);
   int classify(Packet *const p);
   int findslot();
   void set_hash(nsaddr_t src, nsaddr_t dst, int slot);
   int hash(nsaddr_t src, nsaddr_t dst) const {
       u_int32_t s = src ^ dst;
       s ^= s >> 16;
       s ^= s >> 8;
       return (s & 0xff);
   struct hashnode {
       int slot;
       nsaddr_t src;
       nsaddr_t dst;
       hashnode* next;
   hashnode* ht_[256];
   const hashnode* lookup(nsaddr_t src, nsaddr_t dst) const;

int MCastClassifier::classify(Packet *const pkt)
   IPHeader *h = IPHeader::access(pkt->bits());
   nsaddr_t src = h->src() >> 8; /*XXX*/
   nsaddr_t dst = h->dst();
   const hashnode* p = lookup(src, dst);
   if (p == 0) {
        * Didn’t find an entry.
        * Call tcl exactly once to install one.
        * If tcl doesn’t come through then fail.
       Tcl::instance().evalf("%s new-group %u %u", name(), src, dst);
       p = lookup(src, dst);
       if (p == 0)
           return (-1);
   return (p->slot);

The class MCastClassifiermplements a chained hash table and applies a hash function on both the packet source and destination addresses. The hash function returns the slot number to index the slot_ table in the underlying object. A hash miss implies packet delivery to a previously-unknown group; OTcl is called to handle the situation. The OTcl code is expected to insert an appropriate entry into the hash table.

MultiPath Classifier

This object is devised to support equal cost multipath forwarding, where the node has multiple equal cost routes to the same destination, and would like to use all of them simultaneously. This object does not look at any field in the packet. With every succeeding packet, it simply returns the next filled slot in round robin fashion. The definitions for this classifier are in ~ns/, and are shown below:

class MultiPathForwarder : public Classifier {
   MultiPathForwarder() : ns_(0), Classifier() {}
   virtual int classify(Packet* const) {
       int cl;
       int fail = ns_;
       do {
           cl = ns_++;
           ns_ %= (maxslot_ + 1);
       } while (slot_[cl] == 0 && ns_ != fail);
       return cl;
   int ns_; /* next slot to be used. Probably a misnomer? */

Hash Classifier

This object is used to classify a packet as a member of a particular flow. As their name indicates, hash classifiers use a hash table internally to assign packets to flows. These objects are used where flow-level information is required (e.g. in flow-specific queuing disciplines and statistics collection). Several "flow granularities" are available. In particular, packets may be assigned to flows based on flow ID, destination address, source/destination addresses, or the combination of source/destination addresses plus flow ID. The fields accessed by the hash classifier are limited to the ip header: src(), dst(), flowid() (see ip.h).

The hash classifier is created with an integer argument specifying the initial size of its hash table. The current hash table size may be subsequently altered with the resize method (see below). When created, the instance variables shift_ and mask_ are initialized with the simulator’s current NodeShift and NodeMask values, respectively. These values are retrieved from the AddrParams object when the hash classifier is instantiated. The hash classifier will fail to operate properly if the AddrParams structure is not initialized. The following constructors are used for the various hash classifiers:


The hash classifier receives packets, classifies them according to their flow criteria, and retrieves the classifier slot indicating the next node that should receive the packet. In several circumstances with hash classifiers, most packets should be associated with a single slot, while only a few flows should be directed elsewhere. The hash classifier includes a default_ instance variable indicating which slot is to be used for packets that do not match any of the per-flow criteria. The default_ may be set optionally.

The methods for a hash classifier are as follows:

$hashcl set-hash buck src dst fid slot
$hashcl lookup   buck src dst fid
$hashcl del-hash src dst fid
$hashcl resize   nbuck

The set-hash() method inserts a new entry into the hash table within the hash classifier. The buck argument specifies the hash table bucket number to use for the insertion of this entry. When the bucket number is not known, buck may be specified as auto. The src, dst and fid arguments specify the IP source, destination, and flow IDs to be matched for flow classification. Fields not used by a particular classifier (e.g. specifying src for a flow-id classifier) is ignored. The slot argument indicates the index into the underlying slot table in the base Classifier object from which the hash classifier is derived. The lookup function returns the name of the object associated with the given buck/src/dst/fid tuple. The buck argument may be auto, as for set-hash. The del-hash function removes the specified entry from the hash table. Currently, this is done by simply marking the entry as inactive, so it is possible to populate the hash table with unused entries. The resize function resizes the hash table to include the number of buckets specified by the argument nbuck.

Provided no default is defined, a hash classifier will perform a call into OTcl when it receives a packet which matches no flow criteria. The call takes the following form:

$obj unknown-flow src dst flowid buck

Thus, when a packet matching no flow criteria is received, the method unknown-flow of the instantiated hash classifier object is invoked with the source, destination, and flow id fields from the packet. In addition, the buck field indicates the hash bucket which should contain this flow if it were inserted using set-hash. This arrangement avoids another hash lookup when performing insertions into the classifier when the bucket is already known.


The replicator is different from the other classifiers we have described earlier, in that it does not use the classify function. Rather, it simply uses the classifier as a table of n slots; it overloads the recv() method to produce n copies of a packet, that are delivered to all n objects referenced in the table.

To supportmulticast packet forwarding, a classifier receiving a multicast packet from source S destined for group G computes a hash function h(S,G) giving a "slot number" in the classifier's object table. In multicast delivery, the packet must be copied once for each link leading to nodes subscribed to G minus one. Production of additional copies of the packet is performed by a Replicator class, defined in

 * A replicator is not really a packet classifier but
 * we simply find convenience in leveraging its slot table.
 * (this object used to implement fan-out on a multicast
 * router as well as broadcast LANs)
class Replicator : public Classifier {
   void recv(Packet*, Handler* h = 0);
   virtual int classify(Packet* const) {};
   int ignore_;

void Replicator::recv(Packet* p, Handler*)
   IPHeader *iph = IPHeader::access(p->bits());
   if (maxslot_ < 0) {
       if (!ignore_)
       Tcl::instance().evalf("%s drop %u %u", name(),
               iph->src(), iph->dst());
   for (int i = 0; i < maxslot_; ++i) {
       NsObject* o = slot_[i];
       if (o != 0)
   /* we know that maxslot is non-null */

As we can see from the code, this class does not really classify packets. Rather, it replicates a packet, one for each entry in its table, and delivers the copies to each of the nodes listed in the table. The last entry in the table gets the "original" packet. Since the classify() method is pure virtual in the base class, the replicator defines an empty classify() method.

Routing Module and Classifier Organization

As we have seen, an ns node is essentially a collection of classifiers. The simplest node (unicast) contains only one address classifier and one port classifier, as shown in Figure 5.1. When one extends the functionality of the node, more classifiers are added into the base node, for instance, the multicast node shown in Figure 5.2. As more function blocks is added, and each of these blocks requires its own classifier(s), it becomes important for the node to provide a uniform interface to organize these classifiers and to bridge these classifiers to the route computation blocks.

The classical method to handle this case is through class inheritance. For instance, if one wants a node that supports hierarchical routing, one simply derive a Node/Hier from the base node and override the classifier setup methods to insert hierarchical classifiers. This method works well when the new function blocks are independent and cannot be "arbitrarily" mixed. For instance, both hierarchical routing and ad hoc routing use their own set of classifiers. Inheritance would require that we have Node/Hier that supports the former, and Node/Mobile for the latter. This becomes slightly problematic when one wants an ad hoc routing node that supports hierarchical routing. In this simple case one may use multiple inheritance to solve the problem, but this quickly becomes infeasible as the number of such function blocks increases.

The only method to solve this problem is object composition. The base node needs to define a set of interfaces for classifier access and organization. These interfaces should

  • allow individual routing modules that implement their own classifiers to insert their classifiers into the node;
  • allow route computation blocks to populate routes to classifiers in all routing modules that need this information,
  • provide a single point of management for existing routing modules.

In addition, we should also define a uniform interface for routing modules to connect to the node interfaces, so as to provide a systematic approach to extending node functionality. In this section we will describe the design of routing modules as well as that of the corresponding node interfaces.

Routing Module

In general, every routing implementation in ns consists of three function blocks:

  • Routing agent exchanges routing packet with neighbors,
  • Route logic uses the information gathered by routing agents (or the global topology database in the case of static routing) to perform the actual route computation,
  • Classifiers sit inside a Node. They use the computed routing table to perform packet forwarding.

Notice that when implementing a new routing protocol, one does not necessarily implement all of these three blocks. For instance, when one implements a link state routing protocol, one simply implement a routing agent that exchanges information in the link state manner, and a route logic that does Dijkstra on the resulting topology database. It can then use the same classifiers as other unicast routing protocols.

When a new routing protocol implementation includes more than one function blocks, especially when it contains its own classifier, it is desirable to have another object, which we call a routing module, that manages all these function blocks and to interface with node to organize its classifiers. Figure 5.3 shows functional relation among these objects. Notice that routing modules may have direct relationship with route computation blocks, i.e., route logic and/or routing agents. However, route computation MAY not install their routes directly through a routing module, because there may exists other modules that are interested in learning about the new routes. This is not a requirement, however, because it is possible that some route computation is specific to one particular routing module, for instance, label installation in the MPLS module.

A routing module contains three major functionalities:

  1. A routing module initializes its connection to a node through register{}, and tears the connection down via unregister{}. Usually, in register{} a routing module (1) tells the node whether it interests in knowing route updates and transport agent attachments, and (2) creates its classifiers and install them in the node (details described in the next subsection). In unregister{} a routing module does the exact opposite: it deletes its classifiers and removes its hooks on routing update in the node.
  2. If a routing module is interested in knowing routing updates, the node will inform the module via RtModule::add-route{dst, target} and RtModule::delete-route{dst, nullagent}.
  3. If a routing module is interested in learning about transport agent attachment and detachment in a node, the node will inform the module via RtModule::attach{agent, port} and RtModule::detach{agent, nullagent}.

There are two steps to write your own routing module:

  1. You need to declare the C++ part of your routing module (see ~ns/rtmodule.{cc,h}). For many modules this only means to declare a virtual method name() which returns a string descriptor of the module. However, you are free to implement as much functionality as you like in C++; if necessary you may later move functionality from OTcl into C++ for better performance.
  2. You need to look at the above interfaces implemented in the base routing module (see ~ns/tcl/lib/ns-rtmodule.tcl) and decide which one you’ll inherit, which one you’ll override, and put them in OTcl interfaces of your own module.

There are several derived routing module examples in ~ns/tcl/lib/ns-rtmodule.tcl, which may serve as templates for your modules.

Currently, there are six routing modules implemented in ns:

Module Name Functionality
RtModule/Base Interface to unicast routing protocols. Provide basic functionality to add/delete route and attach/detach agents.
RtModule/Mcast Interface to multicast routing protocols. Its only purpose is establishes multicast classifiers. All other multicast functionalities are implemented as instprocs of Node. This should be converted in the future.
RtModule/Hier Hierarchical routing. It’s a wrapper for managing hierarchical classifiers and route installation. Can be combined with other routing protocols, e.g., ad hoc routing.
RtModule/Manual Manual routing.
RtModule/VC Uses virtual classifier instead of vanilla classifier.
RtModule/MPLS Implements MPLS functionality. This is the only existing module that is completely self-contained and does not pollute the Node namespace.

Node Interface

To connect to the above interfaces of routing module, a node provides a similar set of interfaces:

  • In order to know which module to register during creation, the Node class keeps a list of modules as a class variable.

The default value of this list contains only the base routing module. The Node class provides the following two procs to manipulate this module list:

Node::enable-module{[name]} If module RtModule/[name] exists, this proc puts [name] into the module list.
Node::disable-module{[name]} If [name] is in the module list, remove it from the list.
When a node is created, it goes through the module list of the Node class, creates all modules included in the list, and register these modules at the node.
After a node is created, one may use the following instprocs to list modules registered at the node, or to get a handle of a module with a particular name:
Node::list-modules{} Return a list of the handles (shadow objects) of all registered modules.
Node::get-module{[name]} Return a handle of the registered module whose name matches the given one. Notice that any routing module can only have a single instance registered at any node.
  • To allow routing modules register their interests of routing updates, a node object provide the following instprocs:
Node::route-notify{module} Add module into route update notification list.
Node::unreg-route-notify{module} Remove module from route update notification list.

Similarly, the following instprocs provide hooks on the attachment of transport agents:

Node::port-notify{module} Add module into agent attachment notification list.
Node::unreg-port-notify{module} Remove module from agent attachment notification list.

Notice that in all of these instprocs, parameter module should be a module handle instead of a module name.

  • Node provides the following instprocs to manipulate its address and port classifiers:
    • Node::insert-entry{module, clsfr, hook} inserts classifier clsfr into the entry point of the node. It also associates the new classifier with module so that if this classifier is removed later, module will be unregistered. If hook is specified as a number, the existing classifier will be inserted into slot hook of the new classifier. In this way, one may establish a "chain" of classifiers; see Figure 5.2 for an example. NOTE: clsfr needs NOT to be a classifier. In some cases one may want to put an agent, or any class derived from Connector, at the entry point of a node. In such cases, one simply supplies target to parameter hook.
    • Node::install-entry{module, clsfr, hook} differs from Node::insert-entry in that it deletes the existing classifier at the node entry point, unregisters any associated routing module, and installs the new classifier at that point. If hook is given, and the old classifier is connected into a classifier chain, it will connect the chain into slot hook of the new classifier. As above, if hook equals to target, clsfr will be treated as an object derived from Connector instead of a classifier.
    • Node::install-demux{demux, port} places the given classifier demux as the default demultiplexer. If port is given, it plugs the existing demultiplexer into slot port of the new one. Notice that in either case it does not delete the existing demultiplexer.

Commands at a glance

Following is a list of common node commands used in simulation scripts:

$ns_ node [<hier_addr>]

Command to create and return a node instance. If <hier_addr> is given, assign the node address to be <hier_addr>. Note that the latter MUST only be used when hierarchical addressing is enabled via either set-address-format hierarchical{} or node-config -addressType hierarchical{}.

$ns_ node-config -<config-parameter> <optional-val>

This command is used to configure nodes. The different config-parameters are addressingType, different type of the network stack components, whether tracing will be turned on or not, mobileIP flag is truned or not, energy model is being used or not etc. An option -reset maybe used to set the node configuration to its default state. The default setting of node-config, i.e if no values are specified, creates a simple node (base class Node) with flat addressing/routing. For the syntax details see the section on the node configuration interface.

$node id

Returns the id number of the node.

$node node-addr

Returns the address of the node. In case of flat addressing, the node address is same as its node-id. In case of hierarchical addressing, the node address in the form of a string (viz. "1.4.3") is returned.

$node reset

Resets all agent attached to this node.

$node agent <port_num>

Returns the handle of the agent at the specified port. If no agent is found at the given port, a null string is returned.

$node entry

Returns the entry point for the node. This is first object that handles packet receiving at this node.

$node attach <agent> <optional:port_num>

Attaches the <agent> to this node. Incase no specific port number is passed, the node allocates a port number and binds the agent to this port. Thus once the agent is attached, it receives packets destined for this host (node) and port.

$node detach <agent> <null_agent>

This is the dual of "attach" described above. It detaches the agent from this node and installs a null-agent to the port this agent was attached. This is done to handle transit packets that may be destined to the detached agent. These on-the-fly packets are then sinked at the null-agent.

$node neighbors

This returns the list of neighbors for the node.

$node add-neighbor <neighbor_node>

This is a command to add <neighbor_node> to the list of neighbors maintained by the node.

Following is a list of internal node methods:

$node add-route <destination_id> <target>

This is used in unicast routing to populate the classifier. The target is a Tcl object, which may be the entry of dmux_ (port demultiplexer in the node) incase the <destination_id> is same as this node-id. Otherwise it is usually the head of the link for that destination. It could also be the entry for other classifiers.

$node alloc-port <null_agent>

This returns the next available port number.

$node incr-rtgtable-size

The instance variable rtsize_ is used to keep track of size of routing-table in each node. This command is used to increase the routing-table size every time an routing-entry is added to the classifiers.

There are other node commands that supports hierarchical routing, detailed dynamic routing, equal cost multipath routing, manual routing, and energy model for mobile nodes. These and other methods described earlier can be found in ~ns/tcl/lib/ns-node.tcl and ~ns/tcl/lib/ns-mobilenode.tcl.

Previous chapter: "The Class Simulator" | index | Next chapter: "Links: Simple Links"