Network Simulation in J-Sim   (UP)

INET Network Model   (PREV)

Online Interactions   (NEXT)

Part II
Creating Simulation Scenarios

January 20, 2007



0 Overview

With all the components available in INET, one may readily compose a network scenario of his/her like. The basic way to compose a scenario is to build it "by hands". That is, we create every necessary components, from networks, nodes, links to protocols and modules inside a node. And then we connect the components. The idea is very simple but the tasks are repetitive and can get a bit tedious even for a small size of network. Fortunately, we can make both tasks of creating a network topology and building network nodes follow certain patterns and then automate the processes. The utility class drcl.inet.InetUtil and the builder classes drcl.inet.NodeBuilder and drcl.inet.CSLBuilder are developed for this purpose.

Building a network simulation scenario in J-Sim is outlined in the following TCL script:

# Create a container to hold the scenario
cd [mkdir drcl.comp.Component scene]
# Step 1: Create topology
...
# Step 2: Build nodes
...
# Step 3: Configure nodes
...
# Attach simulator runtime to "scene"
attach_simulator .
# Start all "active" components under "scene" if there is any
run .

In what follows, we first describe the process of building a scenario "by hands", and then introduce the utility functions in drcl.inet.InetUtil for creating network topologies (step 1). Followed by that, we introduce the builder classes and their usages (step 2). Finally, we describe other utility functions in drcl.inet.InetUtil for building different network scenarios (step 3).

1 Build by Hands

As a review, the commands we use to build a scenario "by hands" are listed below.

Table 1: The most useful commands to build a scenario "by hands".
Command Description
mkdir Creates a component in the component hierarchy.
connect Connects two components at specified ports.
cd Changes the working component to the component specified.
! Invokes a method of the component specified.

In what follows, we use an example to demonstrate how to use these commands to build a scenario. 

EXAMPLE 1

Fig.1 and Fig.2 is an example which demonstrates how to use these commands to build a scenario. On the right of the figures are the scripts that build the scenarios.

Figure 1: A simple two-node network.
mkdir drcl.inet.Node node0 node1
mkdir drcl.inet.Link link0
connect -c node0/0@ -and link0/0@
connect -c node1/0@ -and link0/1@
! link0 setPropDelay .01; # 10ms
Figure 2: The internal structure of node0 and node1.
cd node0
mkdir drcl.comp.io.Stdout stdout
mkdir drcl.inet.core.PktDispatcher pd
mkdir drcl.inet.core.Identity id
mkdir drcl.inet.core.queue.DropTail q
mkdir drcl.inet.core.ni.PointopointNI ni
connect -c pd/100@up -to stdout/in@
connect -c pd/0@down -to q/up@
connect -c q/output@ -and ni/pull@
connect -c ni/down@ -to ./0@
connect -c ./0@ -to pd/0@down
! pd bind [! id]
! ni setBandwidth 1.0e6; # 1Mbps
cd ../node1
# repeat the above for node1
...
cd ..

2 Create Topologies

2.1 Flat Topology

There is a set of createTopology(...) methods in the drcl.inet.InetUtil class for automating the process, as in Fig.1, of creating a flat topology . The most simplest form of all is as follows:

public static void createTopology(Component network_,
                                  int[][] adjMatrix_);

The network_ argument is where the nodes are to be created in. The most important argument in all the createTopology() methods is the adjacency matrix, adjMatrix_. It is a two-dimensional array. The length of the first dimension, i.e., adjMatrix_.length, is the number of nodes. Each element in the first dimension is a one-dimensional array, which represents the neighbors of the node. The position of a neighbor in this one-dimensional array is the ID of the port that the node uses to connect to the neighbor. Nodes are indexed as 0, 1,..., (adjMatrix_.length-1). The neighbors are represented by their indices. Each node may have a different number of neighbors.  The general form of a adjacency matrix is given below:

	Port index -->	(port 0@)          (port 1@)
 node	(node 0)        neighbor(0,0)      neighbor(0,1)     ... neighbor(0, nb0)
index	(node 1)        neighbor(1,0)      neighbor(1,1)     ... neighbor(1, nb1)
  |	...
  V	(node (n-1))    neighbor((n-1),0)  neighbor((n-1),1) ... neighbor((n-1), nbn-1)

where n is the number of nodes, nbi is the number of neighbors of node i. Fig.3 shows an example topology and its adjacency matrix representation. Two different ways of creating the topology are shown on the right.

EXAMPLE 2

Figure 3: An example topology and its adjacency matrix.
Adjacency matrix 
   (0@) (1@) (2@)
(n0) 1    3    2 
(n1) 3    0      
(n2) 0    3      
(n3) 0    1    2 

      
# Build by hands:
mkdir drcl.inet.Node n0 n1 n2 n3
connect -c n0/0@ -and n1/1@
connect -c n0/1@ -and n3/0@
connect -c n0/2@ -and n2/0@
connect -c n1/0@ -and n3/1@
connect -c n2/1@ -and n3/2@
------------------------------------------------------
# Use InetUtil:
set adjMatrix_ [java::new {int[][]} 4 {{1 3 2} {3 0} {0 3} {0 1 2}}]
java::call drcl.inet.InetUtil createTopology [! .] $adjMatrix_

One may specify a negative number to not use a particular port to connect.  For example, the adjacency matrix below is the same as that in Fig.3 except that now n1 uses 2@ to connect to n0 instead of 1@:

	   (0@) (1@) (2@)
	(n0) 1    3    2 
	(n1) 3   -1    0      
	(n2) 0    3      
	(n3) 0    1    2 

Based on the above idea, a variety of createTopology() methods are derived:

public static void createTopology(Component network_, int[][] adjMatrix_);
public static void createTopology(Component network_, int[][] adjMatrix_, boolean assignAddress_);
public static void createTopology(Component network_, int[][] adjMatrix_, Link link_);
public static void createTopology(Component network_, int[][] adjMatrix_, Link link_, boolean assignAddress_);
public static void createTopology(Component network_, int[][] adjMatrix_, long[] ids_);
public static void createTopology(Component network_, int[][] adjMatrix_, long[] ids_, Link link_);
public static void createTopology(Component network_, Object[] existing_, int[][] adjMatrix_);
public static void createTopology(Component network_, Object[] existing_, int[][] adjMatrix_, boolean assignAddress_);
public static void createTopology(Component network_, Object[] existing_, int[][] adjMatrix_, Link link_);
public static void createTopology(Component network_, Object[] existing_, int[][] adjMatrix_, Link link_, boolean assignAddress_);
public static void createTopology(Component network_, Object[] existing_, int[][] adjMatrix_, long[] ids_);
public static void createTopology(Component network_, Object[] existing_, int[][] adjMatrix_, long[] ids_, Link link_);
public static void createTopology(Component network_, String routerIDPrefix_, String hostIDPrefix_, int[][] adjMatrix_, Link link_);
public static void createTopology(Component network_, String routerIDPrefix_, String hostIDPrefix_, int[][] adjMatrix_, Link link_, boolean assignAddress_);
public static void createTopology(Component network_, String routerIDPrefix_, String hostIDPrefix_, int[][] adjMatrix_, long[] ids_, Link link_);
public static void createTopology(Component network_, String routerIDPrefix_, String hostIDPrefix_, Object[] existing_, int[][] adjMatrix_, Link link_);
public static void createTopology(Component network_, String routerIDPrefix_, String hostIDPrefix_, Object[] existing_, int[][] adjMatrix_, Link link_, boolean assignAddress_);
public static void createTopology(Component network_, String routerIDPrefix_, String hostIDPrefix_, Object[] existing_, int[][] adjMatrix_, long[] ids_, Link link_);

The most complete of all is the following:

public static void createTopology(Component network_,
                                  String routerIDPrefix_,
                                  String hostIDPrefix_,
                                  Object[] existing_,
                                  int[][] adjMatrix_,
                                  long[] ids_,
                                  Link link_,
                                  boolean assignAddress_);

All the above methods invoke this one with the missing arguments given by default values. Each of the argument and its default value are described below.

network_
The component that holds all the nodes and links created.
routerIDPrefix_
The component ID of a node created is set to the concatenation of the ID prefix and the node index in the adjacency matrix. If a node has more than one connection, then it is considered as a router and this argument is used as its ID prefix to construct its component ID. The default value is "n".
hostIDPrefix_
If a node has none or one connection, then it is considered as a host, and its component ID is constructed by concatenating the ID prefix and the node index in the adjacency matrix. The default value is "h".
existing_
One may use existing components in creating the topology. The existing components are put in an array. The indices to this array are the node indices as to adjMatrix_. When creating the topology, a node is retrieved from this array, instead of created anew, only if the index does not excess the size of the array and the corresponding element in this array is not null. Default is a null array, meaning that all the nodes will be created anew.
adjMatrix_
The adjacency matrix. See above.
ids_
One may explicitly specify the ID's for the nodes. The indices to this array are the node indices as to adjMatrix_. If the array is not null, then its values are used, instead of the node indices, to construct the component ID's (concatenating either routerIDPrefix_ or hostIDPrefix_ and the value). Default is null.
link_
The physical link component used to connect two nodes. Default is null to connect nodes directly.
assignAddress_
Set to true if the node indices or ids_are also used to set the network addresses of nodes. Default is true. See the section of Address Assignment for details regarding this address assignment action.

2.2 Hierarchical Network

Building a hierarchical network can be done by building levels of flat networks. It is better illustrated by an example. Fig.4 shows an example of a 3-level hierarchical network. For demonstration purpose, suppose net01, net02 and net03 has the same structure as net00.

EXAMPLE 3

Figure 4: A hierarchical network example.

We can build the network either from top to bottom or the other way around. In the following script, we build the network from bottom (level-2) to top (level-0).

 0 set link_ [java::new drcl.inet.Link]
 1 # Build .LAN3, level-2 network
 2 mkdir drcl.inet.Network .LAN3
 3 set adjMatrix_ [java::new {int[][]} 4 {{1 2 3} 0 0 0}]
 4 java::call drcl.inet.InetUtil createTopology [! .LAN3] $adjMatrix_ $link_
 5 connect -c .LAN3/0@ -and .LAN3/n0/3@
 6 # Build .LAN4, level-2 network
 7 mkdir drcl.inet.Network .LAN4
 8 set adjMatrix_ [java::new {int[][]} 5 {{1 2 3 4} 0 0 0 0}]
 9 java::call drcl.inet.InetUtil createTopology [! .LAN4] $adjMatrix_ $link_
10 connect -c .LAN4/0@ -and .LAN4/n0/4@
11 # Build .net0, level-1 network
12 mkdir drcl.inet.Network .net0
13 cp .LAN4 -d .net0/net10 .net0/net12
14 cp .LAN3 .net0/net11
15 cd .net0
16 # index: n0, n1, n2, 3-net10, 4-net11, 5-net12
17 # n0, n1 and n2 are to be created
18 set existing_ [!! [java::null] [java::null] [java::null] net10 net11 net12]
19 set adjMatrix_ [java::new {int[][]} 6 {{1 2} {0 2 3} {0 1 4 5} 1 2 2}]
20 java::call drcl.inet.InetUtil createTopology [! .] $existing_ $adjMatrix_ $link_
21 connect -c ./0@ -and n0/2@
22 cd ..
23 # Build the final, level-0 network
24 cp .net0 -d net00 net01 net02 net03
25 # index: n0, n1, n2, 3-net00, 4-net01, 5-net02, 6-net03
26 # n0, n1, n2 are to be created
27 set existing_ [!! [java::null] [java::null] [java::null] net00 net01 net02 net03]
28 set adjMatrix_ [java::new {int[][]} 7 {{1 2 6} {0 2 3} {0 1 4 5} 1 2 2 0}]
29 java::call drcl.inet.InetUtil createTopology [! .] $existing_ $adjMatrix_ $link_
Building level-2 networks, .LAN3 and .LAN4 are pretty straightforward. Building level-1 and level-0 networks need to mix in constructed level-2 and level-1 network components using the existing_ argument. Note that in line 18 and 27, we make room for n0, n1 and n2 by putting [java::null]'s in the existing_ array.

3 Builders

After a network topology is created, the next task is to build the nodes. Note that the nodes created during the process of creating a network topology are just empty composite components. We need to put in appropriate protocols and modules to make them functional. One way to do this is, of course, to build them by hands as described in the section of Build by Hands. In this section, we explain how to use builder classes to automate the process. The rationale behind this is very simple. Suppose we have 100 nodes in a network topology. Instead of building them node by node by hands, we first categorize the nodes. Supposedly there should be far less types of nodes in the network. Then we build, by hands, a node template for each type of nodes, and then build the nodes of the same type by duplicating the structure of the template node.

3.1 Node Builder

The NodeBuilder class (drcl.inet.NodeBuilder) has a set of build(...) methods to build the internal structure of nodes. But before we can build other nodes, we need to first build the NodeBuilder itself. Building a NodeBuilder is no difference from building a real node (i.e., adding components and connecting them). But since the relation between the modules in the upper protocol layer (UPL) and the core service layer (CSL) is defined, we can further automate the process by having a set of port naming rules for a protocol. The best way to understand this is to look at the rules below.

Table 2: The naming rules used by NodeBuilder to connecting service and event ports between UPL modules and CSL.
Rules How NodeBuilder Behaves
If a protocol wants to have 
(service), or listen to (event)...
then it must have (port) when sees this port, NodeBuilder
connects it with (port) of CSL
data services down@ <protocol ID>@up
packet arrival events .pktarrival@ .pktarrival@
identity services .service_id@ .service_id@
identity events .id@ .id@
routing table services .service_rt@ .service_rt@
unicast route query .ucastquery@ .ucastquery@
multicast route query .mcastquery@ .mcastquery@
unicast route events .rt_ucast@ .rt_ucast@
multicast route events .rt_mcast@ .rt_mcast@
interface/neighbor services .service_if@ .service_if@
neighbor events (physical) .if@ .if@
neighbor events (virtual) .vif@ .vif@
packet filter configuration service .configswitch@ .configswitch@

If a protocol follows the rules, then we can simply add the protocol to NodeBuilder and NodeBuilder will take care of the connections between the protocol and CSL.

In what follows, we use another example to see more closely how NodeBuilder works.

EXAMPLE 4

To make it more complete, first build the topology using one of the createTopology(...) methods:

# adjacency matrix: (h4 and h5 are hosts)
# (n0) 1 3 2
# (n1) 3 0 4
# (n2) 0 3 5
# (n3) 0 1 2
# (h4) 1
# (h5) 2
set adjMatrix_ [java::new {int[][]} 6 {{1 3 2} {3 0 4} {0 3 5} {0 1 2} 1 2}]
java::call drcl.inet.InetUtil createTopology [! .] $adjMatrix_
Nodes n0-3 are routers and h4-5 are hosts. Suppose each router runs OSPF for unicast routing, and DVMRP and IGMP1 for multicast routing. Each host runs TCP, IGMP and an application. Fig.5 depicts these two types of nodes.

1 For demonstration only.  IGMP does not exist in the current J-Sim release.

Figure 5: The internal structures of the router (a) and the host (b).

(a)

(b)

In the following, we first build the router builder and then the host builder:

Figure 6: Build the nodes.

 1 set routerBuilder [mkdir drcl.inet.NodeBuilder .routerBuilder]
 2 cd $routerBuilder
 3 mkdir drcl.inet.protocol.ospf.OSPF ospf
 4 mkdir drcl.inet.protocol.dvmrp.DVMRP dvmrp
 5 mkdir drcl.inet.protocol.igmp IGMP igmp
 6 connect igmp/.mcastHost@ -to dvmrp/.mcastHost@
 7 cd ..

 At this point, CSL and connections between the modules and CSL are not there yet (in gray). But the above scripts are all we need to do to build the router builder.

 8 $routerBuilder build [! n0-3]

At this point, the router build has built nodes n0-3 with the internal structure shown in the left figure. Specifically, it creates CSL, the service and event ports of CSL based on the needs of UPL modules and then connect the services and events. Finally, it creates "down" ports at CSL and connect them to the corresponding ports, i.e., 0@, 1@ and 2@, at the node.

Same course for building the host below.

 9 set hostBuilder [mkdir drcl.inet.NodeBuilder .hostBuilder ]
10 cd $hostBuilder 
11 mkdir drcl.inet.transport udp
12 mkdir <application class> app1
13 mkdir drcl.inet.protocol.igmp IGMP igmp
14 connect app/down@ -and udp@up
15 connect app/.service_mcast@ -and igmp/.service_mcast@
16 cd ..

17 $hostBuilder build [! h4-5]

The complete set of build(...) methods of NodeBuilder is listed below:

public void build(Object c_);
public void build(Object[] cc_);
public void build(Object c_, CSLBuilder cb_);
public void build(Object[] cc_, CSLBuilder cb_);

The first and the third forms are to build one node or network, while the others are to build multiple nodes/networks. In line 8 and 17 in the previous example, the arguments in calling the build method ("[! n0-3]" and "[! h4-5]") return Object[], that is, the second form is used.

At the end of build(...), NodeBuilder uses CSL builder to build the internal structure of CSL. In the first two forms of the build(...) method, the default CSL builder is used. To apply a different CSL implementation, one may supply the corresponding CSL builder to NodeBuilder by using the third and fourth forms of the build(...) method. The CSL builder is covered in details in the section of CSL Builder.

3.1.1 Node Map

If the UPL modules in a node forms a layered protocol stack, then one may use a node map to further ease the task of building a NodeBuilder. A node map is a plain text which describes the layering relation between modules.  For example, the node map for a UDP application laying on top of UDP at port 1100 is as follows:

udp	17/csl		drcl.inet.transport.UDP
app	1100/udp	Application_Class

And then we can use the loadmap(String) method of NodeBuilder to load a node map into a NodeBuilder.  Each line in a node map corresponds to a module and the syntax is as follows:

<module_id>	<port>/<lower_layer_module_id>	<module_class>

After parsing a line, NodeBuilder creates the module and connects the down@ port of the module to the <port>@up port of the lower layer module.  If the lower layer module does not multiplex/demultiplex, then <port> should be given by "-" (the "minus" sign).  In this case, NodeBuilder connects the down@ port of the module to the up@ port of the lower layer module.

The current implementation of NodeBuilder requires that the lower layer module be defined ahead. The core service layer (csl) is pre-defined.  One may use it as lower layer module without defining it.

If the lower layer module is the core service layer (csl), then we may omit the <port>/csl field if the protocol is known to INET. (INET recognizes many Internet standard protocols such as TCP, UDP, IGMP, DV, OSPF and so on). 

So the following Tcl script using a node map:

set nb [mkdir drcl.inet.NodeBuilder nodeBuilder]
$nb loadmap {
	udp			drcl.inet.transport.UDP
	app	1100/udp	Application_Class
}

is equivalent to the following Tcl script which builds the nodes directly:

set nb [mkdir drcl.inet.NodeBuilder nodeBuilder]
mkdir drcl.inet.transport.UDP $nb/udp
mkdir Application_Class $nb/app
connect -c udp/down@ -and csl/17@up
connect -c app/down@ -and udp/1100@up

There are four more build(...) methods in NodeBuilder for including a node map as an argument:

public void build(Object c_, String nodemap_);
public void build(Object[] cc_, String nodemap_);
public void build(Object c_, CSLBuilder cb_, String nodemap_);
public void build(Object[] cc_, CSLBuilder cb_, String nodemap_);

These methods call loadmap(String) first and then calls the corresponding build(...) methods without the node map argument.

Note that loading a node map overrides the existing structure in the NodeBuilder.  One may use the same NodeBuilder instance to build different types of nodes with different node maps.  See tutorial examples 2, 3 and 4 for using node map as an alternative.

3.1.2 NodeBuilder Properties

Some properties can be set in the NodeBuilder. When the build(...) method is called, these properties are conveyed and set in the nodes built. It is particularly useful when one wishes to set these properties that are common to the nodes being built. The following table lists the properties defined in the NodeBuilder class.

Table 3: Properties of NodeBuilder.
Property "Set" Method Description
interface bandwidth setBandwidth(double) Sets the bandwidth (in bps) of interfaces. The default value applies if non-positive value is given.
interface buffer size setBufferSize(int) Sets the buffer size (in bytes or packets, depending on the buffer mode) of interfaces. The default value applies if non-positive value is given.
interface buffer mode setBufferMode(String mode_) Sets the buffer mode. The mode_ argument could be "byte" or "packet".
interface maximum 
transmission unit
(MTU)
setMTU(int) Sets the MTU (in bytes) of interfaces, including the size of the packet header (if there is any) for link transmission. The default value applies if negative value is given. Giving zero turns off MTU check (e.g., fragmentation in CSL).

3.1.3 Service and Event Ports

In addition to relying on NodeBuilder to create service and event ports at CSL on demands (of UPL modules), one can command NodeBuilder to create some or all of those ports, including "up" ports. To make it happen, one simply adds the service and event ports to the NodeBuilder, and then, when building a node, the NodeBuilder duplicates all the service and event ports on it at the CSL created for the node.

EXAMPLE 5

The following script is the same as in Example 4 except that now we manually add a 100 "up" port and the multicast route event port to the CSL's of all the routers being built. This way, we can listen to the multicast route events later by attaching an instrument component, and maybe inject packets to, or listen to, the 100 "up" port for network diagnosis. 

 1 set routerBuilder [mkdir drcl.inet.NodeBuilder .routerBuilder]
 2 cd $routerBuilder
 3 mkdir drcl.inet.protocol.ospf.OSPF ospf
 4 mkdir drcl.inet.protocol.dvmrp.DVMRP dvmrp
 5 mkdir drcl.inet.protocol.igmp IGMP igmp
 6 connect igmp/.mcastHost@event -to dvmrp/.mcastHost@
 7 mkdir 100@up .rt_mcast@event
 8 cd ..
 9 $routerBuilder build [! n0-3]

3.2 CSL Builder

The CSLBuilder class (drcl.inet.CSLBuilder) is mainly used by the NodeBuilder class to build the CSL component in a Node component. The CSLBuilder class also has a set of build(...) methods to perform the task.  It has been shown in Example 4 that the service and event ports of CSL are created based on the needs of the UPL modules. When building a CSL, the CSLBuilder makes sure that the resulting CSL does provide the specified services and events while includes only a minimal, necessary structure to reduce the memory consumption.

Since there could be different implementations of CSL, each CSL implementation should at least provide a CSLBuilder which knows the implementation. There is a default CSL implementation in the J-Sim package under the drcl.inet.core sub-package. This implementation is explained in Part I. The CSLBuilder for this implementation is discussed in the following subsection.

3.2.1 Default CSL Builder

The drcl.inet.core.CSLBuilder class is the CSLBuilder for the default CSL decomposition explained in Part I. It is also the default CSLBuilder used by NodeBuilder in the build(...) methods that do not have the CSLBuilder argument. The main tasks of this CSLBuilder are: (1) creating necessary subcomponents for the specified services and events; and (2) creating appropriate packet filter banks to form the interfaces of the node. The first task is straightforward because each subcomponent is in charge of one or more services and/or events and no two subcomponents are responsible for the same service or event (one-to-one correspondence). The second task is more complicated if one wishes to specify the structure of the packet filter bank. A packet filter bank structure can be applied to all the interfaces or only a specific interface. If no structure is given, then the CSLBuilder uses a default buffer and a network interface component to form all the interfaces. Several examples are provided below to explain these tasks and also different applications of this CSLBuilder class.

EXAMPLE 6

In this example, we examine how the CSL components of the two types of nodes from Example 4 are built by the CSLBuilder class.

Figure 7: The CSL component of the router from the Example 4. (a) The CSL component before the build(...) method of the CSLBuilder is called. The CSL component is empty at this point. (b) The CSL component after the build(...) method is called.

(a)

(b)

At the end of building a node in the build(...) method, the NodeBuilder calls the build(...) method of CSLBuilder to build the internal structure of the CSL component. Fig.7(b) shows the internal structure of the CSL component of the router being built by CSLBuilder. Specifically, CSLBuilder creates:

  • pd, the PktDispatcher component for the data services ("up" ports) and the route query service (.mcastquery@),
  • id, the Identity component for the identity services (.service_id@),
  • rt, the Routing Table component for the routing table services (.service_rt@),
  • hello, the Hello component for the neighbor events (.if@, .vif@), and 
  • q0-2, ni0-2, since the script in the previous example does not specify the packet filter bank structure, CSLBuilder creates the default buffers (q0-2) and the default network interface components (ni0-2) to form  the interfaces of the node.  

A similar, but simpler, process takes place for building the CSL component of the host as shown in Fig.8 below.

Figure 8: The CSL component of the host from Example 4. (a) The CSL component before the build(...) method of CSLBuilder is called. (b) The CSL component after the build(...) method is called.

(a) (b)

NOTE: Since J-Sim v1.2, the default CSL composition combines the queue and network interface into one QueueNI component.

One can specify particular packet filter, buffer and/or network interface components in forming particular interfaces or all interfaces of the node by adding them to a CSLBuilder instance and calling the build(...) method of NodeBuilder with the CSLBuilder instance as argument. CSLBuilder recognizes those components by the following rules (<i> is the interface index or packet filter bank ID, <j> is the packet filter ID).

Table 4: The naming rules of drcl.inet.core.CSLBuilder for explicitly adding components.
Component ID How the CSLBuilder Behaves
q, ni Applying this buffer or network interface component to all interfaces.
q<i>, ni<i> Applying this buffer or network interface component to interface <i>.
pf<j> Adding this packet filter to all packet filter banks.
pf<i>_<j> Adding this packet filter to packet filter bank <i>.

The packet filter ID is used to order the packet filters in a packet filter bank. Packet filters with smaller ID's are closer to the PktDispatcher component in the protocol stack.

Note that the properties set in NodeBuilder do not apply to the components (i.e., packet filter, buffer and network interface components) that are explicitly added to the CSLBuilder.  One must set the properties explicitly for these added components.

EXAMPLE 7

Working on the same router in Example 4, now we wish to use RED (Random Early Detection) queue instead of the default FIFO queue. The following script achieves this:

 1 set routerBuilder [mkdir drcl.inet.NodeBuilder .routerBuilder]
 2 cd $routerBuilder
 3 mkdir drcl.inet.protocol.ospf.OSPF ospf
 4 mkdir drcl.inet.protocol.dvmrp.DVMRP dvmrp
 5 mkdir drcl.inet.protocol.igmp IGMP igmp
 6 connect igmp/.mcastHost@event -to dvmrp/.mcastHost@
 7 cd ..
 8 set cslBuilder [mkdir drcl.inet.core.CSLBuilder .cslBuilder]
 9 mkdir drcl.inet.core.queue.REDQueue $cslBuilder/q
10 $routerBuilder build [! n0-3] $cslBuilder

Highlighted in boldface are the augments to the script shown in Fig.6. Now the buffer components q0-2 as in Fig.7(b) are REDQueues.

 

EXAMPLE 8

Fig.9 shows a more complicated CSL construction. For a clearer illustration, unrelated details are omitted in this example.

Figure 9: A more complicated CSL construction.
 1 set routerBuilder [mkdir drcl.inet.NodeBuilder .routerBuilder]
 2 # add UPL modules
 3 ...(omitted)...
 4 set cslBuilder [mkdir drcl.inet.core.CSLBuilder .cslBuilder]
 5 cd $cslBuilder
 6 mkdir drcl.inet.core.queue.REDQueue $cslBuilder/q1
 7 mkdir drcl.inet.core.filter.PktFilter1 pf0
 8 mkdir drcl.inet.core.filter.PktFilter2 pf2_1
 9 $routerBuilder build [! n0-3] $cslBuilder

As a result, pf0_0, pf1_0, pf2_0 are PktFilter1 instances, pf2_1 belongs to PktFilter2, and q1 is a RED queue.

Note: PktFilter1 and PktFilter2 are made up for this illustration only.

 

Note that the packet filter pf2_1 in Fig.9 is wired "upwards". The reason is that each packet filter is equipped with an "up" port (up@) and a "down" port (down@). The direction of wiring is determined by the types of these ports. The type of a port may be input only, output only or input/output. The following table lists the valid combinations of the types of the "up" and "down" ports, and the resulting wiring direction.

Table 5: The rules of drcl.inet.core.CSLBuilder for wiring a packet filter.
Packet Filter Wiring Direction
"Up" port "Down" port
input only output only downwards
output only input only upwards
input/output input/output bi-directional

 From the above discussion, we can infer that the type of the "up" port of pf2_1  is output only, and the type of the "down" port is input only. The port types of the "up" and "down" ports of a packet filter may or may not be configurable, depending on the implementation of the packet filter.

4 Other Utility Functions

After the network topology is created and nodes are built, the next task is to configure the simulation scenario. Configuring a scenario may involve assigning addresses to nodes, configuring nodes and other optional tasks such as setting up virtual interfaces and so on. These tasks and the corresponding utility functions are explained in details in the following subsections. 

4.1 Address Assignment

Set Addresses by Component ID

The drcl.inet.InetUtil class offers two types of automatic address assignment functions. The first type is setAddressesByID(...) which sets the address of a node based on the component ID of the node. Basically, this set of functions extracts the numerical part of the component ID to be the network address of the node. For example, for component ID "n11", it assigns 11 as the network address to the node; for "host35", it assigns address 35. The following lists all different forms of the method:

public static void setAddressByID(Node node_);
public static void setAddressByID(Network network_);
public static void setAddressByID(Object[] nodes_);

EXAMPLE 9

To set address for node "n11":

    java::call drcl.inet.InetUtil setAddressByID [! n11];

To set addresses for all the nodes in network "net0":

    java::call drcl.inet.InetUtil setAddressByID [! net0]

To set addresses for all the nodes starting with "n":

    java::call drcl.inet.InetUtil setAddressByID [! n*]

There are another set of methods that do the opposite of setAddressByID(...), that is, they set component ID based on the network address of a node:

public static void setIDByAddress(Network network_);
public static void setIDByAddress(Object[] networksOrNodes_);

What they do is appending the network address to the non-numerical part of the original component ID to form the new component ID. For example, for the component ID "node11" which has network address 35, it creates a new component ID "node35"; for component ID "net0" of a network component which has the network address 1100/2 (a CIDR-based address representation), it creates a new component ID "net12_4". In "net12_4", 12 is the base address of the network, 4 is the maximum number of nodes in this network.

Assign Addresses to a Hierarchical Network

The second type of address assignment functions is for assigning addresses to a hierarchical network that is structured in the same fashion as described in the section of Hierarchical Network:

public static void setAddressByCIDR(Network network_);
public static void setAddressByCIDR(Network network_, long baseAddress_);
public static void setAddressByCIDR(Object[] networksOrNodes_);
public static void setAddressByCIDR(Object[] networksOrNodes_, long baseAddress_);

These methods not only assign addresses to nodes but also networks. A network (drcl.inet.Network) component is associated with a base address and a mask. One can use getNetworkAddr()/setNetworkAddr(long) and getNetworkMask()/setNetworkMask(long) to access these fields, or use the "cat" command on a network component at run-time to observe these fields.

The address assignment in these methods is based on the principle of CIDR. The advantage of CIDR, compared to traditional class-based address assignment, is that one can use variable length subnet masks to assign addresses in unbalanced hierarchical networks, where by an unbalanced network, we mean a network that contains at least two networks with unequal number of nodes. Since it allows variable length subnet masks, one can assign addresses in a more efficient manner, meaning that the number of un-used addresses can be reduced.

Network Representation in CIDR

In CIDR, a network is represented by an address with a network mask in the format of <address>/<mask>, where a network mask is an address that, in the binary form, starts with contiguous 1's and then all 0's thereafter. For example, the network address 10xy2/11002 with x, y in [ 0,1] is equivalent to 10002/11002, and contains only the nodes with addresses in the format of 10xy2 with x, y in [ 0,1]. A network address can also be represented in the format of <address>/<number of 1's>. For example, the above network address can also be represented as 10002/2.

EXAMPLE 10

Fig.10(a) depicts a network system of 3 levels and totally 10 nodes. The system is unbalanced because net0 and net1 contain 6 and 3 nodes, respectively. If a straightforward address allocation method is used to allocate enough bits to each level of the network hierarchy, start from the first level, totally 6 bits are needed. This is done by (i) assigning 2 bits to the three immediate children node0, net0, and net1 of the system; (ii) going down one level and assigning 2 bits to the three immediate children of net0 and net1; and (iii) going down to one level further and assigning 2 bits to the two immediate children of net2 and the three immediate children of net3. A more efficient assignment can be achieved using 4 bits to assign a unique address to each network and address for the same system. One such solution is given in Fig.10(b).

Figure 10: An example network under CIDR-based address assignment.
(a) The example hierarchical network. (b) One possible CIDR address assignment.

 

EXAMPLE 11

To set addresses for all the nodes in the root network "net0":

    java::call drcl.inet.InetUtil setAddressByID [! net0]

Or equivalently with the base address set to zero:

    java::call drcl.inet.InetUtil setAddressByID [! net0] 0

This function is often used with setIDByAddress(...) so that the component ID's and network addresses can reflect to each other:

    java::call drcl.inet.InetUtil setIDByAddress [! net0]

If the resulting address assignment is as in Fig.10(b), then the network address and component ID of each network and node become:

Original Component ID Component ID Network Address
net0 net0_8 00002/1
net1 net12_4 11002/2
net2 net0_2 00002/3
net3 net4_4 01002/2
node0 node8 10002 (8)
node1 node2 00102 (2)
node2 node0 00002 (0) 
node3 node1 00012 (1)
node4 node4 01002 (4)
node5 node5 01012 (5)
node6 node6 01102 (6)
node7 node12 11002 (12)
node8 node13 11012 (13)
node9 node14 11102 (14)

4.2 Node Configuration

One may configure a node after it is built. Several general properties of a node is exposed at drcl.inet.Node to facilitate configuring a node. These properties and the access methods are listed below.

Property Access Methods
Identity/address
long getDefaultAddress()
void addAddress(long addr_)
void removeAddress(long addr_)
Routing table
boolean hasRoutingCapability()
void addRTEntry(RTKey key_, RTEntry entry_, double timeout_)
void removeRTEntry(RTKey key_)
Object retrieveRTEntry(RTKey key_, String matchMethod_)
Interface bandwidth
void setBandwidth(int ifindex_, double bw_)
void setBandwidth(double bw_)
double getBandwidth(int ifIndex_)
Interface buffer size
void setBufferSize(int ifindex_, int bs_)
void setBuffer(int bs_, String mode_)
void setBuffer(int ifindex_, int bs_, String mode_)
int getBufferSize(int ifIndex_)
Interface buffer mode
void setBufferMode(String mode_)
void setBufferMode(int ifIndex_, String mode_)
String getBufferMode(int ifIndex_)
Interface maximum 
transmission unit (MTU)
void setMTU(int ifindex_, int mtu_)
void setMTUs(int mtu_)
int getMTU(int ifIndex_)
General interface 
information
void setInterfaceInfo(int ifindex_, InterfaceInfoif_)
InterfaceInfo getInterfaceInfo(int ifIndex_)
InterfaceInfo[] getInterfaceInfos()
void setInterfaceInfos(InterfaceInfo[] aa_)
int getNumOfInterfaces()
int getNumOfPhysicalInterfaces()

Most of the functions are straightforward. The hasRoutingCapability() method returns true if an appropriate routing table component is installed in the node, false otherwise.

For bandwidth, buffer size, buffer mode and MTU, there are two set(...) methods. One with the interface index argument is to set the property at the specified interface. The other one without the interface index argument is to set the property at all interfaces. There are two buffer modes: "byte" or "packet". In the "byte" mode, the buffer size and the queue length are accounted in bytes, while in the "packet" mode, they are accounted in numbers of packets.

The drcl.inet.data.InterfaceInfo class encapsulates general interface information, including the local address (drcl.inet.data.NetAddress), the addresses (drcl.inet.data.NetAddress) of the neighbors that can be reached at the interface, the size of the outgoing buffer, the bandwidth, the maximum transmission unit (MTU) and so on. The getNumOfInterfaces() method returns the number of interfaces of the node, including virtual interfaces, if there is any.

4.3 Static Routes Setup

Instead of using the node properties to manually set up static route entries along a path, drcl.inet.InetUtil includes a set of setupRoutes(...) methods to automate the task. Given source and destination nodes, the methods compute the unicast or multicast routes with minimum hop count, and then install appropriate route entries in the nodes along the routes. The following lists different forms of the methods:

public static void setupRoutes(Node src_, Node dest_);
public static void setupRoutes(Node src_, Node dest_, String bidirect_);
public static void setupRoutes(Component src_, Component dest_, long destAddr_, long destAddrMask_);
public static void setupRoutes(Component src_, Object[] dests_, long destAddr_);
public static void setupRoutes(Component src_, Object[] dests_, long destAddr_, long destAddrMask_);
public static void setupRoutes(Component src_, Object[] dests_, long srcAddr_, long srcAddrMask_, long destAddr_, long destAddrMask_);

The methods do not distinguish unicast and multicast addresses. It sets up routes between the source node (src_) and the destination node(s) (dest_/dests_) and uses srcAddr_, srcAddrMask_, destAddr_ and destAddrMask_ to create route entries (drcl.inet.data.RTKey). If  srcAddr_is not present, then address 0 with mask 0 are used instead. If destAddr_ is not present, then it uses the default address of the node instead. If the destination address mask is not present, the default mask of all 1's is used.

The examples of using these methods can be found in the subsequent sections.

In addition to the above methods, one may want to set up the unicast routes for all the nodes in the network in one shot using one of the following methods:
public static void setupRoutes(Component network_, int[][] adjMatrix_);
public static void setupRoutes(Component network_, int[][] adjMatrix_, long[] addr_);
public static void setupRoutes(Node[] nodes_, int[][] adjMatrix_, LinkCost linkcost_);
public static void setupRoutes(Component network_, int[][] adjMatrix_, long[] addr_, LinkCost linkcost_);
For details, refer to the class document at drcl.inet.InetUtil.

4.4 Traffic Model/Source Setup

The following methods in drcl.inet.InetUtil are useful in setting up a traffic source using the specified traffic model:

public static Component createTrafficSource(TrafficModel trafficModel_, Node dest_);
public static Component createTrafficSource(TrafficModel trafficModel_, long dest_);
public static Component createTrafficSource(TrafficModel trafficModel_, long src_, long dest_, long tos_);

The method returns a component which, when started, spontaneously sends out packets, the pattern of which conforms to the traffic model specified and all packets are destined to the destination node specified. The component sends out packets at its down@ port and acts as the packet sending service initiator with default forwarding, router alert turned off, TTL=255 and ToS = drcl.inet.InetPacket.DATA (if the first two are used). As a result, it is ready to be hooked up, as a UPL module, with the core service layer.

For most cases, it is convenient to use the following method instead:

public static Component createTrafficSource(TrafficModel trafficModel_, String id_,
	Node src_, Node dest_, long tos_, int protocol_)

This method not only creates a traffic source component as the above three methods do, but also installs the component in the source node right above the core service layer at the <protocol_>@up port.

For available traffic models, refer to the drcl.net.traffic package and the drcl.net document.

EXAMPLE 12

In this example, the topology from Example 4 is used. For convenience, the topology is shown below.

Figure 11: The network topology from Example 4.

In the following (complete) script, we build the topology and the nodes, and add a traffic source at host h4 and a sink (drcl.comp.tool.DataCounter) at host h5.

 1 cd [mkdir drcl.comp.Component scene]
 2 # Create topology:
 3 set adjMatrix_ [java::new {int[][]} 6 {{1 3 2} {3 0 4} {0 3 5} {0 1 2} 1 2}]
 4 java::call drcl.inet.InetUtil createTopology [! .] $adjMatrix_
 5 # Build nodes:
 6 set routerBuilder_ [java::new drcl.inet.NodeBuilder .routerBuilder]
 7 set hostBuilder_ [cp $routerBuilder_ .hostBuilder]
 8 mkdir $hostBuilder_/20@up
 9 $routerBuilder_ build [! n*]
10 $hostBuilder_ build [! h*]

11 # Set up traffic source:
12 set packetSize_ 512
13 set period_ .1;	# send a packet of size 512 bytes every .1 second
14 set trafficModel_ [java::new drcl.net.traffic.traffic_PacketTrain $packetSize_ $period_]
15 java::call drcl.inet.InetUtil createTrafficSource $trafficModel_ "source" [! h4] [! h5] 0 20]

16 # Set up sink:
17 set sink_ [mkdir drcl.comp.tool.DataCounter h5/sink]
18 connect -c h5/csl/20@up -to $sink_/down@
19 # Set up static routes from h4 to h5
20 java::call drcl.inet.InetUtil setupRoutes [! h4] [! h5]

21 attach_simulator .; # attach simulation runtime
22 # start "active" component: the traffic source
23 run .

Line 8 creates a 20 "up" port on the host builder, which makes the host builder create a 20 "up" port at the CSL when it builds a host node. The up port is used in line 15 and 18 to connect with the traffic source and the sink components in h4 and h5 respectively.

After the simulation starts (at line 23), one can observe the number of packets that have arrived at the sink component by using the command "cat h5/sink" in the Tcl terminal.

4.5 Virtual Interface/Tunnel Setup

One can set up a tunnel between two nodes using one of the following methods in drcl.inet.InetUtil:

public static void setupVIF(Node n1_, int vifindex1_, Node n2_, int vifindex2_);
public static void setupVIF(Node n1_, int vifindex1_, Node n2_, int vifindex2_, int mtu_);
public static void setupVIF(Node n1_, int vifindex1_, int mtu1_, Node n2_, int vifindex2_, int mtu2_);

As can be seen from the arguments, a tunnel is determined by two end points. Each is described by the node component and the index of the interface that terminates the tunnel at one end. One may also specify the maximum transmission unit (MTU) to limit the size of packets that come out at the interface.

EXAMPLE 13

Fig.11(a) shows the physical topology of a network. The following script constructs a virtual topology (shown as in Fig.11(b)) on this network using tunnels.

Figure 12: (a) Physical topology. (b) Virtual topology set up by tunnels.

(a)

(b)

...(omitted)... # create topology
...(omitted)... # build nodes
java::call drcl.inet.InetUtil setupVIF [!  n0] 2 [!  n8] 2
java::call drcl.inet.InetUtil setupVIF [!  n0] 3 [! n16] 2
java::call drcl.inet.InetUtil setupVIF [!  n0] 4 [! n24] 2
java::call drcl.inet.InetUtil setupVIF [!  n8] 3 [! n24] 3
java::call drcl.inet.InetUtil setupVIF [! n16] 3 [! n24] 4

4.6 LAN and CIDR-based Hierarchical Network Setup

The drcl.inet.InetUtil class provides a configure(...) method which can be used to set up a LAN or a CIDR-based hierarchical network. The method has the following form:

public static void configure(Network network_);
public static void configure(Object[] networksOrNodes_);

Specifically, it goes through all the nodes in the specified network component and categorizes nodes into hosts and routers by Node.hasRoutingCapability(). When a host is identified, it adds an appropriate route entry to the routing table of the immediate router that the host is connected to. When a router is identified, it installs appropriate local addresses (drcl.inet.data.NetAddress) to the general information (drcl.inet.data.InterfaceInfo) of the interfaces of the router if the router is at the boundary of the network where it resides.

This method replies on the network addresses being properly set for network components. It is usually used with the setAddressByCIDR(...) method.

EXAMPLE 14

This example is continued from Example 3. Here we build the nodes, assign addresses, set component ID's and then configure the hierarchical network and LANs.

Figure 13: (a) The hierarchical network example from Fig.4. (b) After setAddressByCIDR() and setIDByAddress().

(a)

(b)

...(script from Example 3)...
set nb [mkdir drcl.inet.NodeBuilder .nodeBuilder]
mkdir $nb/.if@event
$nb build [!! net?? n?]
java::call drcl.inet.InetUtil setAddressByCIDR [!! net* n?]
java::call drcl.inet.InetUtil setIDByAddress [!! net* n?]
java::call drcl.inet.InetUtil configure [! net*]

Let's inspect node "n12" by the "cat" command:

 1 Node: 12
 2 Interface index ----- interface info
 3 4 ----- local:<12,-8>, peers:<null>, timeouts:<null double array>, MTU=2147483647, BW=8000.0, buffer=2147483647
 4 Number of identities: 1
 5 12 timeout NaN
 6 Routing Table:
 7 (0,8,0)(0,-1,0) 8-{0} -toHostSubnet- timeout:NaN
 8 (0,9,0)(0,-1,0) 9-{1} -toHostSubnet- timeout:NaN
 9 (0,10,0)(0,-1,0) 10-{2} -toHostSubnet- timeout:NaN
10 (0,11,0)(0,-1,0) 11-{3} -toHostSubnet- timeout:NaN

In line 3, we can see that the local address of interface 4 is set to be 12/-8, which is equivalent to 11002/10002 or 10002/10002. It means that if we "look at" n12 from interface 4, then what we see is a network with address 10002/10002 or 8/8. Also we can see, in line 6-10, that route entries for forwarding packets to host h8, h9, h10 and h11 are properly installed. The format of the route entry print-out is:

<RTKey> <next_hop>-<outgoing interfaces> <extension> timeout:<timeout>

See drcl.inet.data.RTKey and drcl.inet.data.RTEntry for details.

 

Network Simulation in J-Sim

<< INET Network Model

Online Interactions >>