Tutorial On Event Driven Simulations of Network Protocols
Simulation Skeleton
Written by Tom Laramee, Dec. 1995

Written by Tom Laramee for use in the senior level ECE course in Computer Networks 597A, and for use in the graduate-level course ECE671.

Presented is a sample protocol simulation skeleton.

[ Tutorial Index | Lecture Notes | Skeleton Simulation Code | Media Access Simulation Code | Data Link Layer Simulation Code ]
A Network Protocol Simulation Skeleton

Perhaps the most difficult way to write a program is to take someone else's code and modify it to do what you want it to do. It is oftentimes easier to rewrite the program from the beginning (and some of you may choose to do this now) so you have a clear understanding of what each function does.

However, this should not be necessary in the case of network simulations. Remember, the point here is to take the emphasis off of the programming to focus on the understanding of the differnt types of events and how they change the state of the system. Following is an overview of what you need to do to take a network simulation skeleton and modify it to simulate a protocol.

Hardware Platform, O/S, Software

The simulation code is written in C - it should be portable to most any platform with a C compiler (Instructions on porting to an O/S such as Windows are beyond the scope of this tutorial).

However, it is designed to run in conjunction with a UNIX script on a UNIX operating system (X-Windows interface).
(This provides for a nice interface.)
(An easy port would be to Linux.)

The operating environment is a Posix compliant operating system with an X-Windows interface, such as ULTRIX, IRIX, or Linux.

The compiler is GCC, the "make" utility is used to link multiple binaries together.

The examples are compiled on the DEC ULTRIX machines @ UMass.

User Background

To successfully complete this task:

  • It is required that the user have experience coding in C.
  • The compiler is GCC. Familiarity with the UNIX utility "makefile" is desirable but certainly not required.
  • It is desirable that the user be familiar with the UNIX operating system - a basic familiarity with common UNIX commands will suffice (cp, mv, cd, touch, echo, rm, cat, chmod, tar, and the "&" operator)
  • It is desirable - but not required - that the user have a basic familiarity with the X-Windows interface, specifically the Ghostview Postscript previewer utility.
  • It is also desirable - but not necessary - that the user have some experience writing/executing UNIX shell scripts.
  • It is also desirable - but not necessary - the the user have some experience with the GUNplot utility available on most Posix compliant operating systems.

NOTE: If you are not familiar with many of these things, it is not as big a problem as it may seem. Expect to learn a lot. Many of these utilities are very powerful - and you don;t need to become a systems expert on them - you just need to have a very basic understanding of how to use them to get what you want. Example are provided for how to use most of the utilities.

Obtaining the necessary files

There are 3 zip files:

Download the 3rd file, gunzip it, and untar it.

NOTE: The contents of this tape archive should reside in their own directory. If it is untarred in the same directory as either the skeleton or the other example, there will be a file collision and some files will be overwritten and lost.

The filelist should be as is listed below:

These are the 7 base files in the skeleton package. Listed below is these same filenames with a brief overview of how each one fits into the skeleton simulation module.

NOTE: For a reference on the UNIX makefile, at your UNIX prompt type

    % man make

...and then

    % man makefile

For a reference on the utility gnuplot, at your UNIX prompt type:

    % man gnuplot
Overview of Files Involved in the Simulation
File: event.h

At a high level:

This is a header file containing external global variable declarations which are used in the functions which are defined in event.c, global data structures, and prototypes for event functions.

These variables, structures, and functions are grouped together as such because they are generic across all event driven simulations for data link layer and media access protocols. (all the protocols you will simulate in this course).

There should be no reason to modify the code in this file (and in event.c). If you are modifying it - chances are either:

  • you are choosing to rename the variables or data structures to match those in your event functions
  • you are making an error :-)

At a lower level:

This file contains:

  • External declarations for variables which are used in the main.c program and in the event.c functions.
  • Global data structures for:
    • An event structure: (event)
    • A queue structure: (proc)
    • An event type: (tEvent)
    • A queue: (tProc)
    • An event pointer: (tEventPtr)
    • A queue pointer: (tProcPtr)
  • Prototypes for the functions found in event.c (described below):
File: event.c

A collection of functions which are generic across all of the protocols you will be simulating this semester.

Functions include:

  • /* allocates the memory for an event record and returns a pointer to the uninitialized record */
    struct event *alloc_evrec(void);
  • /* free the memory associated with an event record pointed to by evptr */
    void free_evrec(struct event *evptr);
  • /* return a ptr to the event record for the next event */
    struct event *get_nextevent(void);
  • /* allocates the memory for a proc record and returns a pointer to the uninitialized record */
    struct proc *alloc_procrec(void);
  • /* free the memory associated with a proc. record pointed to by procptr */
    void free_procrec(struct proc *procptr);
  • /* return pointer to proc rec for the first (oldest) proc. at the queue. */
    struct proc *get_firstproc(int q_id);
  • /* insert an an event into the event list (chronologically ordered) */
    void insertevent(struct event *p);
  • /* insert the record pointed to by proc into the queue specified by the integer parameter queue */
    void insertproc(struct proc *procptr, int q_id);
  • /* Generate a random number from Unif(0,1) */
    double uni(void);
  • /* Compute the poisson distribution of the next arrival according to the arrival rate of */
    /* lambda and set the global variable next to be the time at which the next event occurs */

    void nextarrival( void );
File: main.h

This file is a header file containing user-defined MACROS and prototypes for user-defined functions.

More specifically, user defined events, such as:

#define ARRIVAL 0
#define RE_ARRIVAL 1
#define COLLISION 2
File: main.c

This file contains all of the code which will be modified by the user. There are several functions already defined (some partially coded to demonstrate their intended function):

/* FUNCTIONS WHICH ARE PARTIALLY CODED FOR YOU */

/* ----------------------------  Initialization functions */
short initParams(int argc, char * argv[]);
void initAll(void);

/* ----------------------------  Display information */
void showParams(FILE * fp);
void showCurrentEvent(FILE * fp);
void showEventList(FILE * fp);
void showReport(FILE * fp);
void printStatus(void); */    

/* USER DEFINED FUNCTIONS */

/* ----------------------------  Generate event functions */
void genEvent1( int node );
void genEvent2( int node );
void genEvent3( int node );

/* ----------------------------  Process EVENTS */
void processEvent1( void );
void processEvent2( void );
void processEvent3( void );

This is the program which requires the most modification to to test a specific protocol.

At the highest level, the program is set up like so: (the is the pseudo code version - it just shows, in a mixture of code and english, how the program is set up)

main.c:

#includes
#defines

/* Global variables */

/* function           */
	initParams()          /*  1 = will require modification */
	initAll()             /*  1  */
	showParams()          /*  1  */
	showEventList()       /*  2 = will require NO modification */
	showCurrentEvent()    /*  2  */
	showReport()          /*  1  */
	printStatus()         /*  1  */
	gen_arrival()         /*  2  */
			      
	genEvent1(); 	      /* 3 = will require complete writing */
	genEvent2();          /*  3  */
	genEvent3();          /*  3  */
				
	processEvent1();      /*  3  */
	processEvent2();      /*  3  */
	processEvent3();      /*  3  */ 

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
main()
{
	initParams();
	initAll();
	showParams();

	< loop for X events >
	{
	  < switch the event type >
	  {
	   case EVENT1:  processEvent1();   break;
	   case EVENT2:  processEvent2();   break;
	   case EVENT3:  processEvent3();   break;		
	  }

	< if we're debugging,  print out status information >

	< see if we're done with the simulation >
	}

printReport()
}
/* ^^^^^^^^^^^^^^^^^^ end of main ^^^^^^^^^^^^^^^^^^ */

See "Hints on Modifying the Skeleton To Test A Protocol" for ideas on modifying this program.

File: makefile

This is a static file which links the multiple files together during compilation to produce 1 executable program, which is called "sim" in the skeleton.

There is no need to modify this file unless you add more programs to the simulation. It should be quite clear (by looking at the existing makefile) how to modify it to include a new file for compilation.

File: sim.sh

This is a UNIX shell script which may be modified (enhanced) for more functionality. There are 2 sample scripts provided with the 2 sample protocols which include extensive modifications (if you're looking for ideas).

The script provided is a minimal implementation script which does the following things:

  • Recompiles your program using "make"
  • Runs it several times
  • Collects statistics on the program runs
  • Runs GUNplot on the statistics file
  • Ghostviews the plotted statistics

This is a UNIX shell script which produces a driver file to run GNUplot to produce a plot of data.

A "driver file" contains the instructions for GNUplot on:

  • Where to find the data
  • What to label the axes
  • The type of axes (logarithmic, X-range, Y-range)
  • The type of lines to use when plotting

This file should require modification.

Compiling the simulation

To compile the program, type:

make sim

This will compile the separate programs and generate a single executable program called "sim".

Running the simulation from the command line

To run the simulation (without the script), type:

sim

You will see the following:

No parameters specified on command line...
Using the default parameters...

Initial simulation parameters...
Number of Nodes............: 1
Channel capacity...........: 2.000e+06
System packet arrival rate.: 1.000000
Nodal packet arrival rate..: 1.000000

Simulation results...
Number of Nodes.................: 1
Channel capacity................: 2.000e+06
Throughput......................: 1.000

The simulation doesn't actually run here - no events are processed. What you see is the report on the initial simulation parameters, and then the final simulation report, with nothing actually happening in between.

This is because there are no "events" which have been added to this simulation to process - thus, the "skeleton" concept.

The idea here is that you will add the events which are specific to your protocol, and this code will help you simulation them.

Running the simulation program with the script

To run the program with the script, type:

sim.sh

You will see the following:

	
sim: Command line parameters are as follows
sim:
sim: Description.....command line flag: value
sim: ---------------------------------------------
sim: Number of nodes............(-n): 1
sim: Channel capacity...........(-c): 2.048e6 (bps)
sim: System arrival rate........(-S): 1 (frames per sec)
sim:
sim: Removing old output files...
sim: Creating permanent data files...
sim: Creating GNUplot files to generate the plots...
sim: Compiling...
gcc -o sim main.o event.o -lm
sim:
sim: Successful compilation...
sim: Running for several different channel arrival rates
sim: The number of cycles to cycle for is: 19
sim: Starting channel arrival rate: 1
sim: Ending channel arrival rate: 1000
sim: In steps of: 50
sim:
sim: Hit  to continue...

This shows that the simulation is ready to be run 19 times, for system arrival rates ranging from 1 to 1000 and in increments of 50. The program has been compiled from within the script.

Hit return, and the simulation will run 19 times. As it runs, notice that the system arrival rate is increasing in increments of 50. These parameters can be reset to simulate for any system arrival rate range and using any integer increment value.

What the sim.sh script is doing

Each time the simulation runs, it is producing a 1 line data file called: "a".

This data file has 2 numbers in it:

  • System arrival rate
  • Channel utilization

The file will look something like this:

10.000 1.000

Each time the program runs, the script concatenates this file to a master data file called: data.utilization
( cat a >> data.utilization)

So, after the program is run 5 times, the data.utilization file looks like this:

1.000 1.000
51.00 1.000
101.0 1.000
151.0 1.000
201.0 1.000

This is the data which will be plotted using GNUplot.

sim.sh and GNUplot

The script, sim.sh, calls the other script: sim.gnuplot.sh before it runs the simulation. This script produces a GNUplot driver file which will be used to plot the data in:

data.utilization

The driver file, called: data.plot1, looks like this:

	
set terminal postscript landscape "helvetica" 20
set output "data.plot1.ps" 
set format xy "%g" 
set xlabel "System Arrival Rate" 
set ylabel "Channel Utilization" 
set title "Utilization vs. System Arrival Rate" 
set key
plot "data.utilization" t '(1 nodes)    Util.' with lines 1 

As you can see, this file:

  • Will output a Postscript file which will be called: data.plot1.ps
  • Plots with 2 axes, x and y
  • Label for x axis: System arrival rate
  • Label for y axis, Channel Utilization
  • Title: Utilization vs. System Arrival Rate
  • Has a "key"
  • Uses as it's input the file "data.utilization"
  • Plots the graph using lines

So, when the simulation is done (sim.sh is done running the program 19 times), you will see the following:

sim:
sim: Generating plots using gnuplot...
sim: First

Warning: empty y range [1:1], adjusting to [0.9:1.1]
	
(ignore this error - it will disappear when you write your own simulations)

sim: Removing data files...
sim: Removing GNUplot driver files...
sim:
sim: The plot names are as follows:
sim: data.plot1.ps
sim:
sim: Would you like to ghostview the plots now? (y/n)

If you enter "y", you will see the plot which was generated. This is done using a very popular utility for X-Windows called Ghostview. It is a Postscript previewer.

Hints on Writing Your Own Protocol Simulation
Includes:

These should be all set. All necessary files are included.

Defines:

You may want to have a look at the defines which are already there for you, and you may want to add some more to make life easier.

Global Variables:

Simply put, there aren't enough here. Typcial variables include:

  • packet transmission time
  • propagation delay
  • number of retransmissions

You need to add the variables which are specific to your protocol.

Functions:
initParams();

This function reads in command line parameters and assigns the values to the appropriate global variables. Right now, there are only 2 variables on the command line (inside of sim.sh).
The program call looks like this:

sim -n 1 -S 1

where:

  • S = system arrival rate
  • n = the number of nodes in the system

It is frequently advantageous to pass several arguments on the command line so you don't need to recompile the program each time you run it. Use this function to do that processing (assignment).

initAll():

This function assigns/calculates the rest of the global variable values which may be functions of command line parameters.

For example, nodal arrival rate is a function of system arrival rate, and system arrival rate is a command line parameter. So, after system arrival rate is assigned a value in initParams(), nodal arrival rate can be calculated here.

NOTE: Sandy Hill will tell you it's a good idea to initialize ALL of your variables. :-)

showParams():

This is a function which prints a report of the values of the global variables for your simulation before it starts running.

Add your new variables here.

Change the #define SHOWPARAMS from "stdout" to "NULL" if you don't want to see this report.

showEventList():

This function prints out the current event list. It requires no modification.

showCurrentEvent():

This function may require modification if you add events and want to view their specific attributes in the debugging report. Hint: Look at the event data structures, and this function before modifying it.

showReport():

This function serves 2 purposes:

  1. Prints out data files for collecting statistics.
  2. Prints out a report for the simulation to the screen.

Right now, the data file printed out is: "system arrival rate" vs. "channel utilization"

More can be added.

This function also has local variables which are used to calculate statistics. One example of this is "utilization". More can be added, and then printed.

printStatus():

This is a debugging function which you may want to add to extensively. I use it at the end of the processing of each event to show me what is happening during the simulation.

Then, when I stop the simulation, I can look at a fairly verbose trace file to show me what is going on during my simulation.

gen_arrival():

This is a function which generates Poisson arrivals to your system based on "lambdaS" (the system arrival rate). Assuming your simulations is assuming Poisson arrivals (you are), this function should require no modification.

genEvent1():
genEvent2():
genEvent3():

These functions are empty. They are for you to write and are specific to your protocol. Typical examples of events which need to be generated for a simulation are:

  • gen_re_arrival() generate a rearrival to the channel
  • gen_time_out() generate a time out event
  • gen_collision_event() collision on the channel

For each protocol, there are a finite number of possible events. These must be understood and coded in these functions so they simulate the affect the event has on the system.

For example,

As you write these event functions, you may want to update the #defines in the "main.h" header so they are more intuitive. They are quite generic right now.

For more example, see the 2 sample protocol provided with this tutorial.

processEvent1();
processEvent2();
processEvent3();

These functions are also empty. They are also specific to your protocol. Typically, for each genEvent? function is a corresponding processEvent? function. This should make intuitive sense:

If you generate a "re_arrival" event, then you need to process this event when it occurs. Therefore, you'll have @ least 2 functions:

  • gen_re_arrival();
  • process_re_arrival();

These functions also must attempt to simulate the behavior of the real system.

For example, a process_collision() event for CSMA might look something like:

		
	void process_collision()
	{
	< if any nodes are transmitting,  stop (back-off) >
	< generate a random time to re-arrive to the channel >
	< schedule a re-arrival event for each node based on their back-off time >
	< schedule an "end of collision" event - end of channel jamming >
	}

For more examples, see the 2 simulations provided with this tutorial.

sim.sh:

This script has the potential to be heavily modified. If you aren't too familiar with the UNIX operating system, you may want to keep modifications to a minimum.

Return to the tutorial and examples...