Introduction

Here I describe the use of the diffserv software that I developed. The purpose of this document is to describe what the software does, and how to use it.

This is valid for the patch ds-patch.ns-2.1b6-current.C035.tar.gz and ds-patch-ns-2.1b5.C035.tar.gz

To do:

Software functionality

The software extends the functionality of the ns network simulator [insert ref] to enable diffserv networks to be simulated.

There are three key components to the extensions that I have implemented:

  1. The IP packet header has been modified to include a diffserv codepoint. The codepoint can have a value that is specified within the IETF; so, codepoints such as EF, AF11, etc. are used. The numerical value contained within the codepoint is not exactly that specified within the IETF, since when I was developing the software I was more interested in specifying the traffic classes that can be used rather than the detailed IP header settings
  2. A conditioner component has been added. The conditioner can contain a number of profiles. Each time a packet passes through a conditioner the set of profiles within the conditioner is scanned, and if the packet matches one of the profiles, then it is checked for conformance with the profile. If it is non-conformant, then some action is taken otherwise, it is passed on unmodified. In the conditioner that I have implemented the conditioner drops EF traffic and remarks non-conformant AFx1 traffic down to AFx2.
  3. A scheduler has been added. This scheduler consists of three different queues; one for each of the EF, AF, and BE traffic classes. These queues are serviced using a simple WRR scheduling scheme.

These are the three main components that have been added. There are small extra additions that make this easier to use. For example a new node class called a DSNode has been defined that contains a conditioner. I have also added functionality to easily insert a conditioner between a link and a node.

Installation of the software

The software currently comes as a patch to the ns software. I may see if it is worth integrating into ns in the future.

The patch that I distribute is a simple tar.gz patch. This must be untarred over the existing distribution and compiled. I have only tried this using a clean distribution i.e. I have untarred my patch over an unbuilt distribution of the ns package - it may work if you build ns on it's own, and then try to add my patch later, but the likelihood of success is certainly higher if you choose to untar my patch over a clean ns distribution. I have different patches for ns-2.1b5 and ns-2.1b6-snapshot: both of these seem to work. See http://www.teltec.dcu.ie/~murphys/ns-work/diffserv/index.html for more info.

To compile and run the software just follow the configure and make procedure as you would with the ns distribution alone. My patch includes a new version of Makefile.in that contains info on the new files in my patch.

Using the extra functionality

Configuring an agent to generate traffic marked with a particular DSCP

It is very simple to configure an agent to generate traffic marked with a particular DSCP. The following code creates a TCP/Reno agent and configures it to generate traffic marked with an AF11 DSCP.

 
	set tcpagent [new Agent/TCPReno]
	$tcpagent set-DSCP AF11

A new command has been added to the agent class, which is a parent of the different variations of TCP, and UDP agents that enables the DSCP to be set. As can be seen above, this command is "set-DSCP".

Installing a conditioner

A conditioner can be installed between a link and a node. This can be viewed as being equivalent to installing a conditioner at the input interface to a node. The way that this is done logically is shown in Figure 1. The conditioner is installed before the node because the way the node is implemented, all the traffic enters the node at the same point once the traffic has entered the node, the link it arrived on is difficult to obtain. It is simpler to insert the conditioner between the link and the node to model the behaviour of a conditioner at the node ingress.

Insertion of conditioner in network

Figure 1: Insertion of conditioner in network

An alternative, which is also implemented in this software is have the conditioner in the node, but this can result in lower simulation performance, for reasons that are explained below.

To describe how to insert a conditioner, it is first necessary to describe the profiles.

Defining a profile

The notion of a profile is essential to diffserv. In the diffserv architecture, the customers specify how much of the network resources they will require using a profile. The profile then contains information describing one customers traffic requirements, or a component of their traffic requirements. The profile has two constituent parts the scope definition part, and the resource requirements part. The scope definition part defines what traffic is governed by the profile, and the resource requirements part specifies how much traffic can pass through the conditioner and still be conformant to the profile.

In my implementation, I have included a single type of profile, although I think that the software is sufficiently well structured to make it easy for other types of profiles to be specified. In my implementation I have taken the view that the scope functionality will be the same for any type of profiler, and I have implemented an abstract c++ base class with functionality to determine whether a particular packet falls into the scope of this profile. Then the profiler is a descendent of this class that has token bucket functionality. In this profiler, the token bucket size and the token generation rate are specified. I have named this (probably not very well) a DSPeakRateProfile.

To create a profiler the following things need to be specified the scope and the bucket characteristics. To specify the scope, a number of options are available: the scope can be ONE_TO_ONE, ONE_TO_N, or ONE_TO_ANY. These names are quite descriptive, but for the sake of clarity, they are explained here. A scope of ONE_TO_ONE means that the profile governs the traffic between a single source and destination. A scope of ONE_TO_N means that the profile governs the traffic between a single source and a number of destinations, and a scope of ONE_TO_ANY means that the profile governs the traffic between a source and any destination. This is consistent with the notions in the diffserv documentation.

In previous versions of my patch, the source and destination matching algorithms were very simple: the source parameter could match everything or one source, the destinations were the same, but were more flexible, since a number of destinations could be present in the profile. This has now been improved upon. The address matching algorithm in the DSProfile class now associates a mask with an address. So now the DSProfile class can consider a number of sources to be in scope.

The implementation of the address masking is simple: a mask is specified and this is bitwise ANDed with the address in the packet header as it passes through the profiler. This typically ensures that some bits are reset. The source can then be set to the value of the remaining bits.

For example, the address might be 0x00001000 and the mask might be 0xfffff000. This would match any packets that have a source address of 0x00001xxx. Note that the addressing is different in ns-2.1b5 and ns-2.1b6-current. In ns-2.1b5 the lowest 8 bits of the address are port identifier bits; the remainder identify the node. In ns-2.1b6 a seperate variable is used for the port identifiers, and the address variable contains only node address information.

I still haven't worked out if/how this can work with hierarchical addressing, but my guess is that it can.

The way in which the destination address is matched depends on the value of the scope variable. If the scope is set to ONE_TO_ANY, then any destination is matched. If the scope is ONE_TO_ONE, then only one destination is matched, and if the scope is ONE_TO_N, then some fixed number of destination addresses are compared against the destination address of the packet. Like the source address above, a mask can be supplied with the destination. If no mask is supplied, then a mask of 0xffffffff, which has no effect, is used

A profile can be configured as follows:


	set profile [new DSPeakRateProfile]
	$profile set-scope ONE_TO_ONE
	$profile set src_ $some_addr
	$profile add-dest $some_addr $some_mask
	$profile set peak_ 1Mb
	$profile set bucket_size_ 50000
	$profile set-DSCP AF11
	$ns at 0.0 "$profile start"

This example configures a profile of 1Mb/s. The peak rate is specified in terms of bits/s.

In my implementation, the bucket size is specified in bits. Since the line rate is specified in terms of bit/s and the packets were specified in terms of bytes, I had to make a decision on whether the bucket size would be specified in terms of bits or bytes. It was marginally easier for me to specify the size in bits. The interface to the profile object has changed - the bucket size used to be specified in terms of the time taken to fill the buffer. This has been removed.

It is possible to add a number of destinations when the scope is set to ONE_TO_ONE. In this case, only the first destination in the list of destinations is checked. The others are ignored. A good way to specify addresses is as follows:


	$profile set src_ [$agent set addr_]

if you're using ns-2.1b5, or if you're using ns-2.1b6 snapshot later than mid September 99, you must use the following


	$profile set src_ [$agent set agent_addr_]

due to the change in the ns addressing scheme.

To use the ONE_TO_N scope, just add more destinations these will be searched in the order in which they were added until they are exhausted or a match is found.

Adding a profile to a conditioner

Having made the profile, then we want to add it to a conditioner. This can be done as follows:


	set conditioner [new DSConditioner]
	$conditioner add-profile $profile

Its as simple as that.

Note that the conditioner works by searching through the profiles to see if the current packet matches the scopes of any of the installed profiles. The search stops on the first profile whos scope matches the packet. This profile is used for testing whether or not the packet conforms. Hence if a number of profiles will match a conditioner, the order in which they are inserted into the conditioner matters. They are searched in the order in which they are inserted first inserted is first searched.

The conditioner that I have developed takes specific action on non-conformant packets. Conformant packets are passed through the conditioner unmodified. What happens to non-conformant packets depends on their type for AFx1 packets, they are remarked to AFx2, and for EF packets they are dropped. No action is taken on any other packet types.

Inserting a conditioner into the network

To insert a conditioner into the network use the following:


	$ns insert-conditioner $conditioner $n1 $n2

Note that the order of the nodes is important. This will insert a link between nodes n1 and n2, just before n2. It will apply to traffic going from n1 to n2, and not traffic going in the other direction.

Obtaining feedback from the conditioner

The conditioner contains some counters that keep track of the amount of packets that are non-conformant. Currently, this is not done on a per-profile basis, but rather on an aggregate basis. The counters that currently exist in the conditioner are the following:

To reset these counters, the reset-counters command is used e.g.:


	$conditioner reset-counters

To determine the value of these counters at any time the get-packet-count,get-scoped-packets,get-scoped-ef-packets, get-nonconformant-ef-packets,get-scoped-af-packets, get-nonconformant-af-packets commands are provided. These can be used as follows:


	set pc [$conditioner get-packet-count]
	set sp [$conditioner get-scoped-packets]
	set sep [$conditioner get-scoped-ef-packets]
	set ncep [$conditioner get-nonconformant-ef-packets]
	set sap [$conditioner get-scoped-af-packets]
	set ncap [$conditioner get-nonconformant-af-packets]

Inserting a scheduler into the network

Implementation of the scheduler

In the development of ns, it was decided that the best way to design a link is to associate a queue with it directly. Hence, when a link is created between two nodes the buffer controlling access to the link is part of the link object in ns, rather than the node object, where it is located in the physical world.

When I wanted to implement a new type of access to a link namely a scheduler that has diffserv capabilities I thought that the easiest way to do it was to write a new OTcl method to install a link with a scheduler associated with it. For all the other buffer/queue management schemes, a single method, duplex-link can be used to used to determine what type of queue is required and to add it.

This can be done because all the buffer and queue objects are descendants of the basic queue class. In my case, I wanted to develop a new class which was not a descendant of the queue class. This was because I thought that the queue class was slightly inappropriate for my needs it already contained a particular queue implementation, which was quite different from my needs. So I decided that it was best to create a new class that inherited the properties of the Connector object.

I wanted my scheduler to contain 3 queues one for EF, one for AF and one for BE traffic. I wanted the EF queue to be a simple drop-tail queue, the AF queue to be an RIO queue, and the BE queue to be an RED queue. There is a drop-tail and RED queue implementation in ns, but these seemed to be too complex for my needs. So, I developed some new classes a DSQueue class, a DSREDQueue class and a DSRIOQueue class. I also developed some classes to test the RIO queue, to enable it to be installed on a link this involved writing code to implement Tcl linkage. However, since this was solely for test purposes, I did this quite quickly.

The scheduler contains the three queues. This is the basic DSScheduler class. It is an abstract class, since it doesnt define packets from each of the queues are scheduled and put onto the outgoing link. I developed a child class DSSchedulerWRR that serves these queues using a simple WRR algorithm. This algorithm is not as sophisticated as the WRR algorithm in the ns implementation, since it doesnt support the notion of borrow. However, it seems to work reasonably well.

It would be quite simple to modify the DSScheduler class to contain more queues, and the DSSchedulerWRR class to service these extra queues.

Inserting and configuring a scheduler

Since a scheduler is associated with a link, the way to insert a scheduler into the network is to insert a link with a scheduler associated with it. This can be done using the following code:


	$ns DSSchedulerLink-duplex-link $n1 $n2 $linkrate $delay

This creates two unidirectional links; one from n1 to n2 and one from n2 to n1. Each of these links has a scheduler attached to them.

The next task is to configure the scheduler. The following code can be used to configure the scheduler:


	set scheduler [[$ns link $n1 $n2] queue]
	$scheduler set-ef-queue-length 40
	$scheduler set-af-queue-length 62
	$scheduler set-be-queue-length 150
	$scheduler af-queue-rio-params 0.002 30 60 50 15 30 10
	$scheduler be-queue-red-params 0.002 50 145 20
	$scheduler set ef_queue_weight_ 1
	$scheduler set af_queue_weight_ 3
	$scheduler set be_queue_weight_ 6
	$scheduler set aggregate-bytes-thresh 1000

To configure the scheduler, it is first necessary to get a reference to the scheduler object contained within the link class. This can be done using the first statement above. Note that although the scheduler does not inherit the properties of the C++ Queue class, it is still possible to put it into the link object in the queue field and access it by dereferencing the queue field.

It is possible to set a number of parameters within the scheduler. The queue lengths are quite self-explanatory. The queue weights define the proportions in which the link capacity is to be divided amongst the different classes. Specifically, the queue weights define what fraction of the data in the current round is apportioned to each class. In the above example, 1/10 is given to the EF queue, 3/10 to the AF queue and 6/10 to the BE queue. No direct binding between C++ and Tcl RED and RIO parameters exists for the scheduler. This is primarily because these are specified within an object within the scheduler, rather than as variables within the scheduler itself. These are set by issuing the af-queue-rio-params or be-queue-red-params to the scheduler.

The be-queue-red-params function takes the following parameters in the following order: qweight, the minimum drop threshold and the maximum drop threshold and 1/maxp. The af-queue-rio-params takes the following parameters in the following order: qweight, minimum and maximum drop thresholds for in-traffic 1/maxp for in-traffic, minimum and maximum drop thresholds for out traffic followed by 1/maxp for out-traffic.

The final parameter set above is the aggregate-byte-thresh parameter. To understand this, it is necessary to understand how the WRR operates here. Crucial to the WRR implementation is the notion of a round. At the start of each round, the amount of data to be transmitted on the link is initialised. The amount of data that can be allocated to each queue is easily determined by dividing the total into the ratios as specified by the queue weights. The WRR mechanism works by checking the queue after the last one served and seeing if it has data ready for transmission. If so, and the queue has not exceeded the amount of data its allowed to transmit in this round, then the packet is served. If not, then another queue is found that has data to transmit and has some bytes left in its allowance. If no queue has both packets and bytes left in its allowance, then the amount of untransmitted data in the round is checked. If this is below the aggregate-bytes-thresh, then a new round is started. This will probably mean that any source with a packet will be able to transmit, and then the first one checked that has a packet waiting to be transmitted will be served.

Obtaining information from the scheduler

The scheduler measures and provides an interface to obtain basic packet loss statistics. The scheduler measures the EF, AF and BE drop ratio. These can be obtained as follows:


	$scheduler get-ef-drop-ratio
	$scheduler get-af-drop-ratio
	$scheduler get-af-in-drop-ratio
	$scheduler get-af-out-drop-ratio
	$scheduler get-be-drop-ratio

The scheduler also provides functionality to enable the some of the queue lengths to be obtained at any time throughout the simulation. This can be done as follows:


	$scheduler get-af-curr-length
	$scheduler get-af-curr-in-length
	$scheduler get-be-curr-length
	$scheduler get-be-ewma

The queue length can be obtained for the AF and BE queues. The amount of in-profile AF packets can also be determined, and the EWMA for the BE RED queue can be determined.

Inserting a DSNode

I wanted to implement a node that had a number of inputs and performed aggregate policing on all of these inputs. The way that the conditioners above worked meant that this was not very easy to do. It would have require a configuration as in Figure 2. In this configuration, all the traffic is multiplexed onto the same link, and then the conditioning is applied before the packets enter the next node.

Aggregate policer configuration

Figure 2: Configuration that enables an aggregate policer to be used

This configuration is not the most efficient it requires the use of extra nodes and links. A simpler solution is to develop a new type of node that contains a conditioner that can be used to police all the traffic entering the node. This is shown in Figure 3. This requires definition of a new type of node one with a conditioner installed.

Alternative aggregate policer implementation

Figure 3: Another implementation of an aggregate policer

To implement this, I developed a new type of node called a DSNode. This is a Tcl class that contains a conditioner as well as the other components of a node. It is a child class of the Tcl class Node. The class only supports one extra command. This command is add-profile, and can be used to add a profile to the conditioner in exactly the same manner as a profile is added to a conditioner above. Here is some example code:


	set n1 [new DSNode]
	$n1 add-profile $p

Conclusion

I have described the modification that I made to the ns code to implement diffserv functionality. I have given examples of the use of this code.