Skip to content

jlegatheaux/cnss

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

71 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CNSS Overview ( Computer Networks Simple Simulator ) and User Manual

CNSS is a network simulator, written in Java, developed for teaching purposes, to make it possible to simulate computer networks and simple networking protocols. The simulation results are deterministic and repeatable, making it easier to reproduce and interpret them. This simulator is developped and maintained by me with the help of members of the systems group of the Departament of Informaticas of the Faculty of Sciences of the NOVA Lisbon University (FCT/UNL).

CNSS was inspired by a simulator, developed around 2001, by Adam Greenhalgh, from the University College of London. That simulator was mainly intended for testing routing algorithms based on the distance vector principle, and was limited to networks with zero transit time links.

CNSS is capable of simulating any routing algorithm, as well as simple transport and application protocols and algorithms running on heterogeneous nodes. To that end, CNSS leverages a more realistic notion of link, characterised by transmission and propagation delays as well as a packet loss rate. Morevoer, in CNSS, a network can be comprised of different types of nodes, capable of executing user provided control (for routing purposes) and other protocols and algorithms, in a more generic fashion.

Finally, to allow a link to also simulate an overlay provided link, made of a set of links and packet switches, it is also possible to introduce jitter in a link if one so desires. If the jitter is high, it may even introduce random packet reordering. It is also possible to limite the size of the income queues of links.

CNSS in short

CNSS simulates a packet switched network where each processing step is instantaneous, i.e., executed without advancing the virtual clock, while communications make that clock advance. Virtual time resolution is 1 millisecond, i.e., the clock advances 1 by 1 ms. All nodes and links update their state in a lock step way, according to a logically, perfectly synchronised global notion of time. The network nodes state changes from a state to the following one by executing processing steps. The main concepts of the simulator are presented next.

A network is composed of a set or mix of nodes, interconnected by links. Nodes exchange packets among themselves.

In each clock tick, all nodes execute a processing step by consuming the events scheduled to them for this processing step. These events may be user defined clock ticks, alarms, links state changes, requests for state printing or packet deliveries to the node. The execution of a processing step may generate future events like alarms and delivery of messages to the same or other nodes, etc.

Nodes execute a common kernel that triggers processing steps execution using up calls (upcalls from now on) to the methods of two algorithms (Java classes) defined by the users: a control algorithm and an application algorithm. These algorithms may be different in each node. Upcalls processing steps can send messages, set alarms, etc. by using node kernel interface down calls (downcalls from now on). The system imposes that the execution of each node is structured as the execution of two automata: a control automaton, controlled by the control algorithm, and an application automaton, controlled by the application algorithm. The figure below depicts the structure of a CNSS node.

Network configuration is defined by a configuration file using simple commands to add nodes and links and their parameters. It also allows the introduction of special events to be triggered off at specific time steps, as well as the setting of simulation or algorithms parameters.

The real execution progress of the simulation is bound to the time required to execute the nodes processing steps. Therefore, nodes processing steps cannot execute blocking actions, which would block the simulator. Reading and writing local files is acceptable as well as any other quick execution method calls, but using Java calls like Thread.sleep() or any kind of synchronization is fully discouraged.

Next, packets, nodes, links and the configuration file are presented in more detail.

Packets

Packets are objects of the class Packet. This class has several subclasses among which DataPacket, ControlPacket and TracePacket, each representing different types of packets. Data packets are sent and received by ApplicationAlgorithms. Control packets are sent and received by ControlAlgorithms. Data and Control packets can be understood as if each CNSS node would support two fixed IP ports. One addressing a traditional OS kernel, and the other addressing a single application executed by the node.

Trace packets are sent at times defined by the configuration file and perform a function similar to the tracing of the route followed by a packet from an origin to a destination. Trace packets are transparent to the nodes algorithms and are automatically processed by nodes kernels (see below). User defined code shoud avoid instantiating and sending trace packets.

Packets have several fields, namely the following:

protected int src;  // the initial sending node
protected int dst;  // the destination node
protected int ttl;  // packets time-to-live
protected PacketType type; // DATA, CONTROL, ...
protected int seq;  // sequence number
protected int size; // size of the full packet including header and payload
protected byte[] payload; // the payload of the packet

Some contants in the Packet class have speacial meaning for the CNSS notion of Packet: Packet.HEADERSIZE = 20 is the size of the header to mirror IPv4 packets size and Packet.INITIALTTL = 32 is the default value of packets TTL.

Nodes

Nodes execute two user defined algorithms, an application algorithm and a control algorithm, both structured as an automaton executing actions associated with a pre-defined set of events types, each one called an upcall.

Among the most important upcalls are: initialise(int now, ...), on_clock_tick(int now), on_receive(int now, DataPacket p), on_timeout(int now) and several others. Nodes automata may choose to use clock_ticks, if so, their periodic value in millisecends should be returned by the initialise(...) upcal. If the initialise method returns 0, no clock_ticks will be delivered to this algorithm.

Each upcall, but the initialise one, is triggered by the delivery of an event that got to the node. All events that should be triggered in the same processig step (characterized by the same value of the clock) are delivered in sequence without any predefineded specified order. Initialization of nodes and their algorithms takes place before the simulation starts.

The definition of the interfaces of the two algorithms executed by nodes are presented next.

ApplicationAlgorithm Interface

The ApplicationAlgorithm interface should be implemented by any class whose instances are intended to implement application automata executed by nodes. The class that implements this algorithm must have a zero argument constructor. All methods have, as first argument, the virtual time of the processing step where the event fired. The methods of this interface are the following:

public int initialise(int now, int node_id, Node nodeObj, String[] args);

Initialises the application algorithm or automaton and returns the desired control_clock_tick_period. Input parameter now has the value 0. If the returned control_clock_tick_period is equal to 0, no clock_ticks will be delivered to the algorithm.

Parameters: id is this node id, nodeObj is a reference to the node object kernel executing this algorithm, args is an array of arguments specified in the configuration file (see the configuration file section).

public void on_clock_tick(int now);

Signals a clock tick event.

public void on_timeout(int now);

Signals a timeout event.

public void on_receive(int now, DataPacket p);

Given a data packet from another node, here it is and process it! Parameter: p the received packet.

public void showState(int now);

Prints application state table(s) to the screen in a user defined previously agreed format. This up call is called at each time step where there is a correspondent event directed to the node in the configuration file (see its section below).

The node processing steps application algorithm can use public methods of the class `DataPacket as well as the following down calls:

nodeObj.createDataPacket (int destination, byte[] payload)
nodeObj.send(DataPacket p)
nodeObj.set_timeout(int t)

When a packet is created, its sequence number is 0. In order to guarantee that packet sequence numbers are different (relative to each node), packets must be created using nodeObj.createDataPacket(…) and nodeObj.createControlPacket(...) methods, which take care of providing unique sequence numbers. Therefore, all algorithms must avoid creating packets directly (using a constructor of the respective class) when maintaining packets sequence numbers uniqueness is important.

The order by which upcalls are executed in the same processing step is not defined and may not be used to establish the correctness of an algorithm. This order is implementation defined and is, in general, related with the order by which the originating events were created. The current implementation fires timeouts upcalls after all the other upcalls of the same processing step. However, reception of a packet and firing of a timeout in the same processing step is impossible, since the reception of the packet will cancel the timeout.

ControlAlgorithm Interface

The ControlAlgorithm interface should be implemented by any class whose instances are intended to implement a control automata to be executed by nodes, implementing, for example, the way the node routes packets (not directed to itself). The class that implements this algorithm must have a zero argument constructor. All upcall methods have as first argument the virtual time of the processing step where the event fired. First are apresented some of the interface constants, and then the methods.

static final int LOCAL = Node.LOCAL;       // the number of the virtual loop back interface
static final int UNKNOWN = Node.UNKNOWN;   // means an inexistent or unknown interface 
public int initialise(int now, int node_id, Node nodeObj, GlobalParams parameters, Link[] links, int nint);

Initializes the control algorithm and returns the desired control_clock_tick_period. Input parameter now has the value 0. If the returned control_clock_tick_period is equal to 0, no clock_ticks will be delivered to the algorithm. Interfaces are numbered 0 to nint-1 (which is always equal to links.size()). Each has a link attached: links[i]. Interface LOCAL, with value -1, is virtual and denotes the local loop interface.

Parameters: id is this node id, nodeObj is a reference to the node kernel object executing this algorithm, parameters is the collection of global parameters (see the configuration file section), links is the nodes links array, nint is the number of interfaces (or links) of this node. The method must return the requested clock_tick_period value.

public void on_clock_tick(int now);

Signals a clock tick event.

public void on_timeout(int now);

Signals a timeout event.

public void on_receive(int now, Packet p, int iface);

Given a control packet from another node, here it is, process it! Parameter: p is the packet received, int is the interface by which the node received the packet.

public void forward_packet(int now, Packet p, int iface);

Given a packet of any type destinated to another node, forward it to the appropriate interface by using the downcall nodeObj.send(Packet p, int iface). Parameters are: p is the packet to forward, iface is the interface from where this node received that packet. If it is not possible to forward the packet, deliver it using nodeObj.send(Packet p, Node.UNKNOWN) since, this way, the node will correctly count all dropped packets.

An alternative way of packet discarding is by fully ignoring it and not sending it. In this case, the downcall countDroppedPacket() should be used to allow its drop counting by the node's kernel.

public void on_link_up(int now, int iface);
public void on_link_down(int now, int iface);

Signals a link up or down event. Parameter: iface the interface (link) that changed state.

public void showControlState(int now);

Prints control algorithm state table(s) to the screen in a previously user defined agreed format.

public void showRoutingTable(int now);

Prints control algorithm routing table to the screen in a previously user defined agreed format.

These up calls are called at each time step where there is a correspondent event directed to the node, see the section on the configuration file.

The node processing steps control algorithm can use the following down calls:

nodeObj.send(DataPacket p)
nodeObj.send(Packet p, int iface)
nodeObj.set_control_timeout(int t)

nodeObj.createDataPacket (int receiver, byte[] payload)
nodeObj.createControlPacket (int sender, int receiver, byte[] payload)
nodeObj.getInterfaceState(int iface)

nodeObj.getId();
nodeObj.countDroppedPacket();

The node control algorithm processing steps can use the public methods of the following objects:

class Packet
class Link (*)
class Node (*)
class Parameters (*)

(*) only the public methods that do not write the state of these objects.

When a packet is created, its sequence number is 0. In order to guarantee that packet sequence numbers are different (relative to each node), packets must be created using nodeObj.createDataPacket(…) and nodeObj.createControlPacket(...) methods, which take care of providing unique sequence numbers. Therefore, both algorithm must avoid creating packets directly (using a constructor of the respective class) when maintaining packets sequence numbers uniqueness is important.

The order by which upcalls are executed in the same processing step is not defined and may not be used to establish the correctness of an algorithm. This order is implementation defined and is, in general, related with the order by which the originating events were created. The current implementation fires timeouts upcalls after all the other upcalls of the same processing step. However, reception of a packet and firing of a timeout in the same processing step is impossible, since the reception of the packet will cancel the timeout.

Links

The model of CNSS link is very simple: a link is point to point (connects exactly two nodes) interconnection with two extremes, end 1 and end 2, each directly connected to an interface of a (in general) different node. Links are charactetized by:

private long bwidth = 1000;  // in bits per second - bps
private int latency = 0;     // in ms
private double errors = 0.0; // packet drop rate probability
private double jitter = 0.0; // propagation time variation
private int max_queue = 1000000; // max size of its two input queues
private boolean up;   

Besides these variables, links have two queues at each end: an out queue or output queue, and an in queue or input queue. Packets that were sent during a processing step, are queued in the out queues of the node's links. At the end of each processing step, all these packets are consumed and become delivery events associated with the other extreme of the link, and will be delivered when the corresponding delivery time (processing step) arises.

Delivery times are computed using the time required to transmit the packet, added to the propagation time, added to the delivery time of the newest queued packet in the same in queue.

Link parameters error and jitter randomly constraint the way the link performs as well the delivery time of packets in the later case. For example, probability of packets being dropped is 1% if the link errors parameter is equal to 0.01. Additionally, propagation time is randomly distributed in the interval [latency,(1+jitter)*latency]. Therefore, if latency is 50 and jitter 0.1, propagation time randomly varies between 50.0 and 55.0.

Packets can also be discarded if their destination in queue is full. A system parameter, see below, allows the definition of the size of all in queues of the network. Here, size is defined as the number of packets waiting delivery in an in queue.

Network definition and simulation configuration file

To start a simulation, a configuration file must be given as parameter, as in the example below:

java -cp bin cnss.simulator.Simulafor config.txt

Virtual time is in milliseconds, starts at 0 and ends at a value that can be changed in the configuration file. Its limit is Integer.MAXVALUE which corresponds to around two million seconds, or more or less 555 hours of virtual time.

This file is structured as a sequence of lines of text. These lines obey a simple syntax. In the current version, tokens must be separated by exactly one space chracter. The different possible configuration file lines are the following.

Parameters

parameter name value 

Defines a global parameter of name name and value value (both are character strings without any blanck character in the middle); global parameters are accessible to nodes ControlAlgorithms as a collection of ( name, value ) pairs accessible via an hash map collection.

System parameters

parameter stop 100000  

This parameter defines the duration of the simulation in virtual ms. It is good practice to make this one of the first lines of the config file. This special parameter is directly recognized by the simulator.

When the corresponding processing step execution ends, the simulation is aborted, even if there are events to be delivered after it. If it is the case, the simulator warns the user by printing the number of such events. These may be related to packets not yet delivered, or to timers to be triggered later. It is always the case in situations were clock_ticks are being used, since they have already been scheduled to occur later before the abortion takes place.

If it is not defined, a huge default value is assumed. This is armless if nodes algorithms do not use clock_ticks. Otherwise all clock_ticks will be delivered up to the end of the simulation max duration.

parameter max_queue 100  

This parameter defines the max size of in queues of all links in the network. If it is not defined, a huge default value is assumed.

Examples of user-defined parameters

parameter trace
parameter filter
parameter drop_duplicates
parameter splithorizon
parameter expiration
parameter max_ttl 5

which can be used to parametrize control algorithms. The ones without an attached value may be used as flags like in:

boolean drop_duplicates = parameters.containsKey("drop_duplicates");

Nodes

node node_id #interfaces name_of_control_class name_of_application [class args …]

Node ids must start at 0 and follow a strict increasing order. Arguments are accessible to the node application algorithm via a String[] args parameter of the initialise() method.

Example:

Node 1 5 FloodSwitchAlgorithm SwitchAppAlgorithm hello world

Links

link side1_node.side1_interface side2_node.side2_interface bandwidth latency errors jitter [ state ]

Example:

link 0.0 1.0 10000000 10 0.01 0.0 down

introduces a link from interface 0 of node 0 to interface 0 of node 1 with a 10 Mbps bit rate, with 1% error rate, 0.0 jitter and starting in state down.

In each node, links ids must start at 0 and follow a strict increasing order.

Configuration defined events

The configuration file can also introduce several types of events to be fired at stated time steps. Each one may be introduced using a 3-tuple (event name, time of event, event parameters). Here are examples of all the available such events.

traceroute 12000 origin_node destination_node
dumpappstate 8000 [ all | node id ]
dumproute 1000 [ all | node id ]
dumppacketstats 120000 [ all | node id ]
uplink / downlink 18000 link_origin link_destination
dumpcontrolstate 8000 [ all | node id ]

The first one sends a tracing packet at time = 12000 from from_node to destination_node. Tracing packets are directly recognized by nodes kernels and allow tracing the path from origin to destination.

The dumpappstate one delivers a dumpappstate event at time = 8000 to the Application Algorithm of all nodes or to a specific one.

The dumproute one delivers a dumproute event at time = 1000 to the Control Algorithm of all nodes or to a specific one.

The dumppacketsstats one delivers a dump packet statistics event at time = 120000 to all nodes or to a specific one.

The uplink / downlink ones delivers an uplink event or a down link event at time = 18000 to the Control Algorithm of all nodes or to a specific one.

The dumpcontrolstate one delivers a dumpcontrolstate event at time = 8000 to the Control Algorithm of all nodes or to a specific one.

Finally, a line starting with ´#´ is considered a comment.

In the configuration file, the character case of the first token, the command, is not relevant. For example, writing 'node' or writing 'NoDe' produces the same result. The same is true for events to be fired. 'dumpPacketStats' or 'dumppacketstats' produces the same result. It is also possible to use underscrores as separators while writing events names, as shown in the table below, where each row shows equivalent forms of writing the same token.

Original name Using case to highlight Using underscores
traceroute TraceRoute trace_route
uplink UpLink up_link
downlink DownLink down_link
dumpappstate DumpAppState dump_app_state
dumproute DumpRoute dump_route
dumppacketstats dumpPacketStats dump_packet_stats
dumpcontrolstate DumpControlState dump_control_state

Example of a simulation

Configuration file

The following configuration file is used i this example.

# Simple network: 2 nodes and one switch

parameter stop 8000   # 8 seconds

node 0 2 cnss.lib.FloodingSwitch cnss.lib.EmptyApp
node 1 1 cnss.lib.EndSystemControl cnss.examples.Sender
node 2 1 cnss.lib.EndSystemControl cnss.examples.Receiver

Link 0.0 1.0 1000000 50 0 0
Link 0.1 2.0 1000000 50 0 0

dumpAppState 8000 all
dumpPacketStats 8000 1
dumpPacketStats 8000 2

It defines the network of the figure below. Two nodes, node 1 and 2, are connected to a switch, node 0. Links have 1 Mbps bandwidth and 50 ms of propagation time. The configuration file sets the end of the simulation to 8000 ms and schedules events dump_app_state to be delivered to all nodes, and events dump_packet_stats to be sent to nodes 1 and 2, all at simulation time = 8000, the end of the simulation. The code of the different classes is shown below as well as the output of the simulation execution.

Next we present the classes used in the example. Class Sender is a simple sender application that sends a packet to the Receiver every second. Its initialise(...) method returns 1000, i.e. the value of the interval between clock ticks. Whenever it receives a packet, it prints its value using the method log(...). Whenever it receives a dump_app_state event, the node kernel calls the showState()upcall, that prints the numbers of reply packets received.

package cnss.examples;

import cnss.simulator.ApplicationAlgorithm;
import cnss.simulator.DataPacket;
import cnss.simulator.Node;
import cnss.simulator.Packet;
import cnss.simulator.DataPacket;

public class Sender implements ApplicationAlgorithm {

	private Node nodeObj;
	private int nodeId;
	private String[] args;

	private String name = "sender";
	private boolean logOn = true;
	private	int count = 0;

    
	public Sender() {
	}

	public int initialise(int now, int node_id, Node mynode, String[] args) {
		nodeId = node_id;
		nodeObj = mynode;
		this.args = args;

		log(now, "starting pings");
		return 1000;
	}

	public void on_clock_tick(int now) {
		count++;
		byte[] message = ("ping "+count).getBytes();
		DataPacket p = nodeObj.createDataPacket(2, message);
		log(now, "sent ping packet n. "+count+" - " + p);
		nodeObj.send(p);
	}

	public void on_timeout(int now) {
		log(now, "timeout");
	}

	public void on_receive(int now, DataPacket p) {
		log(now, " received reply \"" + new String(p.getPayload()) + "\"");
	}

	public void showState(int now) {
		System.out.println(name + " received "+count+" replies to pings");
	}

	// auxiliary methods

	private void log(int now, String msg) {
		if (logOn)
			System.out.println("log: " + name + " time " + now + " node " + nodeId + " " + msg);
	}

}

Class Receiver implements the receiver node algorithm, which only prints the contents of each received packet and replies to the sender, similat to a ping reply. It also prints the number of received messages when it receives a dump_app_stat event signaled via a showState() upcall.

package cnss.examples;

import cnss.simulator.ApplicationAlgorithm;
import cnss.simulator.DataPacket;
import cnss.simulator.Node;

public class Receiver implements ApplicationAlgorithm {

	private Node nodeObj;
	private int nodeId;
	private String[] args;

	private String name = "receiver";
	private boolean logOn = true;
	private	int counter = 0;

	public Receiver() {
	}

	public int initialise(int now, int node_id, Node mynode, String[] args) {
		nodeId = node_id;
		nodeObj = mynode;
		this.args = args;

		log(now, "started listening");
		return 0;
	}

	public void on_clock_tick(int now) {
		log(now, "clock tick");
	}

	public void on_timeout(int now) {
		log(now, "timeout");
	}

	public void on_receive(int now, DataPacket p) {
	    counter++;
		String msg = name + " received \"" + new String(p.getPayload()) + "\"";
		log(now, msg);
		// Reply to sender
		DataPacket reply = nodeObj.createDataPacket(p.getSource(), msg.getBytes());
		nodeObj.send(reply);
	}

	public void showState(int now) {
		System.out.println(name + " replyed to "+counter+" ping messages");
	}

	// auxiliary methods

	private void log(int now, String msg) {
		if (logOn)
			System.out.println("log: " + name + " time " + now + " node " + nodeId + " " + msg);
	}

}

The example also illustrates how control algorithms can be used to forward packets. Class EndSystemControl is a control algorithm that implements packet forwarding for a node with one only link (and interface). If the interface from which the packet came is the virtual local loop interface, and the node link is up, the packet is forwarded to its interface. The node kernel only calls the upcall forward_packet() of the control algorithm to forward a packet whose destination is not the local node, thus, it is useless to test if the destination of the packet is not the node itself.

Whenever it is not possible to forward the packet, for example because the interface is down, the downcall send() is still called with UNKNOWN as the value of the interface used to forward the packet. This allows the kernel of the node to count dropped packets. If tracingOn == true, the algorithm prints a trace of its execution to help the simulation users to understand what is going on.

Class EndSystemControl can only adequately forward packets of nodes using a kind of default route, i.e. with one only interface. Therefore, during its initialization, it tests this condition to avoid simulation users the trouble of using this control algorithm inadequately. This method also returns 0 since the algorithm does not requires the use of periodic clock ticks.

package cnss.lib;

// the control (routing) of an end system with one only interface

import cnss.simulator.ControlAlgorithm;
import cnss.simulator.GlobalParameters;
import cnss.simulator.Link;
import cnss.simulator.Node;
import cnss.simulator.Packet;

public class EndSystemControl implements ControlAlgorithm {
	
	private Node nodeObj;
	private int nodeId;
	private GlobalParameters parameters;
	private Link[] links;
	private int numInterfaces;
	private String name="end system";
	private boolean tracingOn = false;

	public EndSystemControl() {
		
	}

	public int initialise(int now, int node_id, Node mynode, GlobalParameters parameters, Link[] links, int nint) {
		if ( nint > 1 ) {
			tracingOn = true;
			trace(now,"end system has more than one interface");
			System.exit(-1);
		}
		nodeId = node_id;
		nodeObj = mynode;
		this.parameters=parameters;
		this.links=links;
		numInterfaces=nint;
		return 0;
	}
	
	
	public void on_clock_tick(int now) {
		trace(now,"clock tick");
	}
	
	public void on_timeout(int now) {
		trace(now,"timeout");
	}
	
	public void on_link_up(int now, int iface) {
		trace(now,iface+" link up");
	}
	
	public void on_link_down(int now, int iface) {
		trace(now,iface+" link down");
	}
	
	public void on_receive(int now, Packet p, int iface) {
		trace(now,"received control packet");
	}
	
	public void forward_packet(int now, Packet p, int iface) {
		if ( iface == LOCAL && links[0].isUp() ) { // locally sent packet
		    // always sends a copy, not the Packet object itself
		    nodeObj.send(p.getCopy(),0);
			trace(now, "forwarded a locally sent packet");
			return;
		} 
		if ( iface == LOCAL && ! links[0].isUp() ) {
			nodeObj.send(p,UNKNOWN);
			trace(now, "network interface is down");
			return;
		}
		// allows the node to count dropped packets
		nodeObj.send(p,UNKNOWN);
	}

	public void showControlState(int now) {
		trace(now,"has no state to show");
	}
	
	public void showRoutingTable(int now) {
		trace(now,"has no routing table to show");
	}

	// auxiliary methods
	
	private void trace (int now, String msg) {
		if ( tracingOn ) System.out.println("-- trace: "+name+" time "+now+" node "+nodeId+" "+msg);
	}
}

Class FloodingSwitch is another example of a control algorithm. This one implements a flooding switch and therefore can only be used in a network without cycles. Most of its upcall methods are identical to the ones of the previous algorithm but the upcall forward_packet. All identical methods are not shown.

Method initialise() does not tests the number of interfaces of the node and also returns 0. The upcall forward_packet() does the flood, i.e. it sends a copy of the packet to all interfaces whose state is up but the one from which the packet came.

Both control algorithms shown always send a copy of the packet to be forwarded, not the packet object itself. Sending the object may introduce hard to debubg errors. That would be more error prone in this case since the algorithm implements a real flood.

If this control algorithm is used in a network with cycles, a broadcast storm of duplicate packets would arise. The EndSystem control algorithm only forwards locally originated packets (interface == LOCAL) and therefore drops packets received by the node whose destination is not the node.

public class FloodingSwitch implements ControlAlgorithm {

	private Node nodeObj;
	private int nodeId;
	private GlobalParameters parameters;
	private Link[] links;
	private int numInterfaces;
	private String name = "flooding switch control: ";
	private boolean tracingOn = false;

	public FloodingSwitch() {
	}

	public int initialise(int now, int node_id, Node mynode, GlobalParameters parameters, Link[] links, int nint) {
		nodeId = node_id;
		nodeObj = mynode;
		this.parameters = parameters;
		this.links = links;
		numInterfaces = nint;
		return 0;
	}

	public void forward_packet(int now, Packet p, int iface) {
		int copiesSent = 0;
		// do the flood
		for (int i = 0; i < links.length; i++) {
			if (i != iface && links[i].isUp()) {
			    // always send a copy of p, not the object itself
			    nodeObj.send(p.getCopy(), i);
			    copiesSent++;
			}
		}
		if (copiesSent == 0) { // allows the node to count dropped packets
			nodeObj.send(p, UNKNOWN);
		}
		trace(now, "forwarded " + copiesSent + " packet copies");
	}

}

Class EmptyApp() is not shown. This application algorithm does nothing and is provided in the library to be used as application algorithm of switches that run no application code, i.e. all upcalls are empty.

Below, the result of the simulation is shown.

java -cp bin cnss.simulator.Simulator configs/simpleNet.config.txt 
Loading configuration : configs/simpleNet.config.txt
Reading file configs/simpleNet.config.txt
Created Node 0: 2 interf.s, ctr code: cnss.library.FloodingSwitch app code: cnss.library.EmptyApp
Created Node 1: 1 interf.s, ctr code: cnss.library.EndSystemControl app code: cnss.examples.Sender
Created Node 2: 1 interf.s, ctr code: cnss.library.EndSystemControl app code: cnss.examples.Receiver
Added link to node 0 - Link (Node1:0 I1:0)<-->(Node2:1 I2:0) bwd: 1000000 bps lat: 50 ms error %: 0.0 jit %: 0.0 up
Added link to node 1 - Link (Node1:0 I1:0)<-->(Node2:1 I2:0) bwd: 1000000 bps lat: 50 ms error %: 0.0 jit %: 0.0 up
Added link to node 0 - Link (Node1:0 I1:1)<-->(Node2:2 I2:0) bwd: 1000000 bps lat: 50 ms error %: 0.0 jit %: 0.0 up
Added link to node 2 - Link (Node1:0 I1:1)<-->(Node2:2 I2:0) bwd: 1000000 bps lat: 50 ms error %: 0.0 jit %: 0.0 up

simulation starts - first processing step with clock = 0

log: sender time 0 node 1 starting pings
log: receiver time 0 node 2 started listening
log: sender time 1000 node 1 sent ping packet n. 1 - src 1 dst 2 type DATA ttl 32 seq 1 size 26
log: receiver time 1100 node 2 receiver received "ping 1"
log: sender time 1200 node 1  received reply "receiver received "ping 1""
log: sender time 2000 node 1 sent ping packet n. 2 - src 1 dst 2 type DATA ttl 32 seq 2 size 26
log: receiver time 2100 node 2 receiver received "ping 2"
log: sender time 2200 node 1  received reply "receiver received "ping 2""
log: sender time 3000 node 1 sent ping packet n. 3 - src 1 dst 2 type DATA ttl 32 seq 3 size 26
log: receiver time 3100 node 2 receiver received "ping 3"
log: sender time 3200 node 1  received reply "receiver received "ping 3""
log: sender time 4000 node 1 sent ping packet n. 4 - src 1 dst 2 type DATA ttl 32 seq 4 size 26
log: receiver time 4100 node 2 receiver received "ping 4"
log: sender time 4200 node 1  received reply "receiver received "ping 4""
log: sender time 5000 node 1 sent ping packet n. 5 - src 1 dst 2 type DATA ttl 32 seq 5 size 26
log: receiver time 5100 node 2 receiver received "ping 5"
log: sender time 5200 node 1  received reply "receiver received "ping 5""
log: sender time 6000 node 1 sent ping packet n. 6 - src 1 dst 2 type DATA ttl 32 seq 6 size 26
log: receiver time 6100 node 2 receiver received "ping 6"
log: sender time 6200 node 1  received reply "receiver received "ping 6""
log: sender time 7000 node 1 sent ping packet n. 7 - src 1 dst 2 type DATA ttl 32 seq 7 size 26
log: receiver time 7100 node 2 receiver received "ping 7"
log: sender time 7200 node 1  received reply "receiver received "ping 7""
sender received 7 replies to pings
receiver replyed to 7 ping messages
Pkt stats for node 1 :  s 7 r 7 d 0 f 0
(Node1:0 I1:0) s 7 r 7<-->(Node2:1 I2:0) s 7 r 7
Pkt stats for node 2 :  s 7 r 7 d 0 f 0
(Node1:0 I1:1) s 7 r 7<-->(Node2:2 I2:0) s 7 r 7
log: sender time 8000 node 1 sent ping packet n. 8 - src 1 dst 2 type DATA ttl 32 seq 8 size 26

simulation ended - last processing step with clock = 8000

In the first lines, the processing of the configuration file contents is shown, namely the creation of nodes and the instalation of links. Now, the simulation starts. At the first clock tick, a first packet is sent, 100 ms later it gets to the destination, the receiver replies and more 100 ms later the reply gets to the sender. This is so because transmition time is negligible (to transmit 26 bytes at 1Mbps bit rate) and only the links latency accounts for the end to end transit time. The packet has a size of 26 bytes: 20 for the header, as in IP, and 6 to represent the value of the counter (as a character string). The origin and destination nodes, as well as the sequence numbers and the original value of the TTL are also shown.

At the last processing step, when the value of the clock is 8000, nodes reveive the events stated in the configuration file. Only the sender and the receiver show their state, since the switch has nothing to show. After, all nodes print the number of packets sent and received as well as the number of packets sent and received through each of its links. The sender and receiver nodes sent and received 7 packets each, and these packets were also sent and received by their links. It is also noted that these links never droped packets. It is interesting to note that the sender still sends a new packet during this processing step, but as this happens after the execution of the show status and show stats upcalls, this packet is not considered in nodes and links counters.

The above shown algorithms are build as classes that implement the ApplicationAlgorithm and the ControlAlgorithm interfaces. That way the code shows all the required details.

Package library also contains two abstract classes, AbstractApplicationAlgorithm() and AbstractControlAlgorithm(). Theses classe may be used to write application and control algorithms that extend them, what results in a more concise coding style. For example, using the AbstractApplicationAlgorithm the Sender class could become simpler (we also do not show counting the number of packets sent and the processing of received packets):

import java.util.Arrays;
import cnss.simulator.*;
import cnss.lib.*;

public class Sender extends AbstractApplicationAlgorithm {

  public SenderNode() {
      super(true, "sender");
  }

  public int initialise(int now, int node_id, Node self, String[] args) {
    super.initialise(now, node_id, self, args);
		return 1000;
	}

  public void on_clock_tick(int now) {
      self.send(self.createDataPacket( 1, new byte[0]));
  }
} 

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages