- Back -

Physical Implementation of New Protocol

  Introduction  


This chapter will deal with the physical implementation of the protocol. The stages of implementation will be illustrated in each section below, a different section for each extra functionality or requirement added.
Figure 11 Illustrating where the new protocol will sit in the DoD four layer model-

The first question is at which network layer will the protocol sit on? Figure 11 shows an updated diagram of the DoD four layer network model that includes the new protocol. The protocol will be on the same level as TCP and UDP as it too is a transport protocol.

-

Figure 12 Object Hierarchy of the Network Simulator

Figure 12 shows the object Hierarchy in NS. As the new agent that is being added is in the transport layer it is also on the same level (Transport Layer) as UDP and TCP in the Object hierarchy.
4.1.1 Naming the new protocol

The protocol was called “RelUDP”, as it is similar to UDP in the way it uses small packets/Datagrams, but has added functionality such as reliability. Thus RelUDP basically stands for Reliable User Datagram Protocol.

  Fulfilling Requirements and Functionality  

The requirements for the new RelUDP protocol were discussed in the last chapter. Here is a list reviewing these requirements
 Reliability
 Retransmission
 Use Datagram to transport data
 HOL blocking absent
 Minimal Congestion Control

These requirements were not implemented all at once. The process of adding functionality to the protocol was a long and tedious one. Each function or requirement was first designed, then implemented and finally tested and possibly redesigned if necessary. Therefore the whole Development can be seen as a series of stages.

  Implementing a Basic Datagram protocol  

 

The first goal or stage of implementation was to get a basic Datagram protocol coded and working, similar to UDP. The protocol was implemented as a two-way agent. A two-way agent is symmetric in the sense that it represents both a sender and receiver.
The first thing done was to create a packet header. This is basically a structure in C++ that represents the packet object that the protocol uses.

  Packets and Packet Headers  

 

The fundamental unit of exchange between protocols in simulations is an object called a Packet. This packet object that is written in C++ provides enough information to link a packet on to a queue, figure out where the packet is being sent, where it is coming from etc. When designing a new protocol it is good practice to define a new packet header or maybe extend existing headers with additional fields to suit the protocols needs.
New packet headers are created by
 Defining a new C++ structure with the needed fields.
 Defining a static class to provide OTcl linkage.
 Modifying the simulator initialisation code to assign a byte offset that points to where the new header is to be located in the packet relative to others.

The next task is to design a new packet header that will be used with the new protocol “RelUDP”.

struct hdr_RelUDP {
u_int32_t srcid_;
int seqno_;

/* per-field member functions */
u_int32_t& srcid() { return (srcid_); }
int& seqno() { return (seqno_); }

/* Packet header access functions */
static int offset_;
inline static int& offset() { return offset_; }
inline static hdr_RelUDP* access(const Packet* p) {
return (hdr_RelUDP*) p->access(offset_);}
};

 

  Packet Header of the RelUDP Protocol  

 

Figure 13 shows the Packet header designed for the new protocol.
The RelUDP header will require a source identifier field and a sequence number field. This structure shown, hdr_RelUDP defines the basic layout of the RelUDP packet header. It defines the fields are needed and how big they are. The compiler uses this structure to compute byte offsets of the fields. One important thing to note is that there is no objects of this structure type are ever directly allocated. The member functions shown provide a layer of abstraction that is used by other network objects wishing to read or modify the header fields of packets. Another important thing to note regarding the Packet object is that when a packet is created it has every possible packet type (including the packet type’s header fields) in the object. Thus the static class variable offset_ is used to locate the byte offset at which the new header is located. Two methods are provided to utilise this variable to access this header in any packet. These methods are offset() and access().
offset() is used by the packet header management class and should seldom be used.
access() is used to access a particular header in a packet; for example hdr_RelUDP::access(p) accesses the packet header of the RelUDP header.

Now that the new header is defined the next step is to create the basic functionality that the protocol needs which will be implemented in the RelUDP protocol file (RelUDP.cc).
The fundamental functionality of a Basic Datagram Protocol would include the ability to send a packet of a certain set size, segmenting if necessary and also to receive the packet on the other end. This basic functionality is implemented in the functions sendmsg() and recv() respectively. Before the implementation of these two methods is described, the TclClass Hierarchy and the oTCL linkage must be explained.

  TclClass – Mirrored Hierarchy  

 

The base class called TclClass is a pure virtual class. Any classes derived from this base class provide the following two functions:
1. They construct the interpreted class hierarchy to mirror the compiled class hierarchy.
2. They provide methods to instantiate new TclObjects.
The mirrored Object hierarchy has already been discussed in earlier chapters.
Each such derived class is associated with a particular compiled class in the compiled class hierarchy, and can instantiate new objects in the associated class. For the new protocol, a static class derived from the base TclClass is needed to create this mirrored hierarchy.

static class RelUDPAgentClass : public TclClass {
public:
RelUDPAgentClass() : TclClass("Agent/RelUDP") {}
TclObject* create(int, const char*const*) {
return (new RelUDPAgent());
}
} class_RelUDP_agent;


Figure 14 Creation of the RelUDPAgentClass

Figure 14 illustrates the creation of this static class “RelUDPAgentClass”, and is associated with the class RelUDPAgent. This class will instantiate new objects in the class RelUDPAgent.

Figure 15 shows the compiled class hierarchy for RelUDPAgent is that it derives from Agent that in turn roughly derives from TclObject. Concentrating on Figure 14 again, this shows the static class “RelUDPAgentClass”. This class defines the constructor,

RelUDPAgentClass() : TclClass("Agent/RelUDP") {}


Figure 15 Tcl Class Hierarchy

and one additional method, to create instances of the associated TclObject.

TclObject* create(int, const char*const*) {
return (new RelUDPAgent());


Figure 16 The Tcl Object create() returning Tcl objects in the class RelUDPAgent

When the Simulator is first started, it will execute the RelUDPAgentClass constructor for the static variable class_RelUDP_agent, which in turn sets up the appropriate methods and the interpreted class hierarchy. This class is associated with the class RelUDPAgent, thus it creates new objects in this associated class. Another thing to note is that the RelUDPAgentClass::create() method returns TclObjects in the class RelUDPAgent. Therefore when a user is writing a Tcl script to run a simulation includes the new protocol ( new Agent/RelUDP), the method RelUDPAgentClass::create() is invoked.

  oTcl – Linkage  

As already mentioned in a previous chapter, it is possible to link variables between the two languages used (C++ and oTcl). This allows C++ variables to be accessible through Tcl during the simulation, which allows greater flexibility and control of the simulation. This is accomplished in the class's constructor:

RelUDPAgent::RelUDPAgent() : Agent(PT_RelUDP), seqno_(-1)
{
bind("packetSize_", &size_);
}


Figure 17 Variable Linkage

As Figure 17 depicts the linking of C++ variable size_ and OTcl instance variables packetSize_, this grants the user the ability to adjust the packet size during simulation.
4.2.1.4 sendmsg() method

This method is the most complicated method of the simple Datagram protocol that is being implemented. The main aim of this method is to send a Datagram to the destination. sendmsg() takes in three parameters.
 int nbytes specifies the number of bytes that is being sent to the destination
 AppData* data allows the user to attach user application data in the packet.
 const char* flags allows the user to include optional string flags that could be used by an application

Void UdpAgent::sendmsg(int nbytes, AppData* data, const char* flags)
{
Packet *p;
int n;
if (size_)
n = nbytes / size_;
else
printf("Error: RelUDP size = 0\n");

// If they are sending data, then it must fit within a single packet.
if (data && nbytes > size_)
{
printf("Error: data greater than maximum RelUDP packet size\n");
return;
}
while (n-- > 0) {
p = allocpkt();
hdr_cmn::access(p)->ptype() = PT_RelUDP;
hdr_cmn::access(p)->size() = size_;
hdr_RelUDP* relUDPHdr = hdr_RelUDP::access(p);
relUDPHdr->seqno() = ++seqno_;
target_->recv(p);
}
n = nbytes % size_;
if (n > 0) {
p = allocpkt();
hdr_cmn::access(p)->ptype() = PT_RelUDP;
hdr_cmn::access(p)->size() = n;
hdr_RelUDP* relUDPHdr = hdr_RelUDP::access(p);
relUDPHdr->seqno() = ++seqno_;
target_->recv(p);
}
idle();
}


Figure 18 RelUDP sendmsg() method

Figure 18 shows the implementation of this method. Firstly the method performs some simple error checking at the start of the method. The size_ variable is the current size of the packet, i.e. the number of bytes that the packet can hold. This is checked to be greater than zero, or basically if the variable has been set. If this variable has been set, it calculates how many segments if any that the data must be sent in. It then checks if the user has included data that it will fit in the packet. Segmentation is dealt with here if necessary. If the data must be segmented, the while loop sends n packets, n being the number of segments needed. The actual process of sending a packet is quite simple. First a packet p, which has already been declared at the start of the method, is allocated space and created. Then the packet type is selected. This is where we are using the packet header that was created earlier. The size of the packet is set, the sequence number is incremented and finally the packet is sent. It is the target_->recv(p); which actually simulates the sending of a packet. What it is actually doing is calling the recv method of target_. The target_ variable is a pointer to the destination protocol/node. After any segmentation is needed, the remainder of the data (if any) is sent exactly the same way described above.
4.2.1.5 recv() method

As already mentioned the most complicated method in the Basic Datagram protocol that is being implemented was the sendmsg() method. The recv() method is quite simple. Firstly it takes in two parameters;
 Packet* pkt, the packet that is being received
 Handler* the handler which handles the event

Void UdpAgent::recv(Packet* pkt, Handler*)
{
if (app_ )
{
// If an application is attached, pass the data to the app
hdr_cmn* h = hdr_cmn::access(pkt);
app_->process_data(h->size(), pkt->userdata());
}
Packet::free(pkt);
}


Figure 19 RelUDP recv() method

Figure 19 shows the implementation of the recv() method. Firstly the method checks whether the user added user application data in the packet, and if so it passes it to the application to process the data. If on the other hand the user did not add any application data to the packet, the packet is freed from memory and dropped.


The majority of the basic Datagram protocol was explained above. The functionality of the protocol was very basic. It simply sends data in Datagram packet and segments the data if necessary. The next goal is to add reliability to the protocol implementation.

  Implementing Reliability  

The basic Datagram Protocol described in the last section is a simple but effective one. As it is it is lacking functionality that is needed in the new Protocol. One of the basic functions or services that must be added is reliability. The importance of having a reliable Protocol is described in the last chapter.
Providing data reliability consists of two basic components.
 Loss detection
 Loss Recovery
The oncoming sections deal with these two basic components in more detail.
4.2.2.1 Loss Detection
In the basic Datagram Protocol there is no way of knowing whether a Datagram packet actually got delivered to the destination node. The Protocol basically sends the Datagram Packets and hopes for the best. The first aim is to detect a Packet loss.
In order to do this a new Packet must be created. This Packet is called an Acknowledgement packet. Is basic purpose is to inform the sender that a certain Packet has been delivered successfully. The size of an Acknowledgement is considerably smaller than a regular Datagram Packet, thus Acknowledgements have little effect on Congestion. The first goal is to design a new Packet Header similar to the Datagram Packet header that was designed earlier.

struct hdr_RelUDPAck {
u_int32_t srcid_;
int seqno_;

/* per-field member functions */
u_int32_t& srcid() { return (srcid_); }
int& seqno() { return (seqno_); }

/* Packet header access functions */
static int offset_;
inline static int& offset() { return offset_; }
inline static hdr_RelUDPAck* access(const Packet* p) {
return (hdr_RelUDPAck*) p->access(offset_);}
};


Figure 20 RelUDP Acknowledgement Packet Structure

Figure 20 shows the Acknowledgement Packet header designed for the new protocol. This header will be very similar to the RelUDP header that was designed earlier. It also will require a source identifier field and a sequence number field. This sequence number field will be used to match a Packet sent to an Acknowledgement Packet received.
As the Acknowledgement Packet Header is now designed the next goal is to modify the revc() method in the Basic Datagram Protocol so that when it receives a packet, it sends an Acknowledgement Packet back to the original sender, thus acknowledging the Packet. The modifications to the recv() method must enable the protocol to receive both regular RelUDP Packets and RelUDPAck Packets and to differentiate between them.

void RelUDPAgent::recv(Packet* pkt, Handler *h)
{
hdr_cmn* cho = hdr_cmn::access(pkt);
if((cho->ptype() == PT_RelUDP)) // if it is a normal RelUDP packet
{
//We want to create a new RelUDPAck packet and
//send it acknowledging the packet
Packet *np = allocpkt();
//access the common header of the new packet
hdr_cmn* nch = hdr_cmn::access(np);
//sets the size of the new packet to 12 as it is an Ack
nch->size()=12;

//access the IP header fields of the packet being received and
//the new packet being created
hdr_ip * iph = hdr_ip::access(pkt);
hdr_ip * newiph = hdr_ip::access(np);

//switch the Destination and source of the packet that was
//received and set these values to the new packet
newiph->dst()=iph->src();
newiph->flowid()=iph->flowid();
newiph->prio()=iph->prio();

hdr_RelUDP* relUDPHdr = hdr_RelUDP::access(pkt);
nch->ptype() = PT_RelUDPAck;

hdr_RelUDPAck* relUDPAckHdr = hdr_RelUDPAck::access(np);
relUDPAckHdr->srcid()=addr();
relUDPAckHdr->seqno()=relUDPHdr->seqno();

target_->recv(np);
Packet::free(pkt);
}…

 


Figure 21 Revised RelUDP recv() method

Figure 21 shows the modifications needed to the recv() method so that it can handle basic RelUDP Packets. Basically what is needed in this recv() method is the ability to reply by sending an Acknowledgement Packet, when a RelUDP Datagram Packet is received. The first thing that is done in this method is to check the type of the incoming Packet. If the packet type is RelUDP, then the method must send an Acknowledgement back to the node that sent the RelUDP Packet. This is done as follows. First a new Packet is created (np) and its size is set to 12 bytes. Note that this is considerably smaller than a regular Datagram Packet size. The IP header fields are then accessed to figure out where the Packet has come from. Then the new Packets IP header destination address is set to the incoming packets source address, thus switching the direction of the Packet. The packet type is set to RelUDPAck as it is an Acknowledgement and finally the sequence number of the incoming packet is copied to the sequence number of the new Acknowledgement Packet. The acknowledgement is sent.

At the moment the protocol will send a Datagram, and on receiving a Datagram will send an acknowledgement back to the sender. The issue of Packet loss detection is quite not fully implemented. The next step is to implement a mechanism that records the packets sent in a buffer. Thus this buffer could be used with the incoming Acknowledgements to figure out which Packets indeed were lost in the network and require retransmission.

void RelUDPAgent::recordPacket(Packet* pkt)
{
packetsSent[packetIndex_]=pkt->copy();
packetIndex_++;
}


Figure 22 RelUDP recordPacket() method

Figure 22 shows the implementation of the recordPacket() method. The basic aim of this method is to record the Packet pkt in the buffer. The buffer is actually a packet array called packetsSent. One thing that must be explained is the packetIndex_ variable. This variable keeps count of the amount of Packets in the buffer. It is a public class variable, thus each instance of the protocol that is created contains this variable. The copy() method is used to copy the contents of the packet into a Packet array ( buffer). This method is called when a RelUDP packet is being sent. Thus all Datagram packets sent are recorded in the buffer. It is with the help of this method that the protocol can detect packet loss.

As all packets being sent are recorded in the buffer, one possibility of detecting Packet loss would be to remove the corresponding (same sequence number) Packet when an Acknowledgement arrives. If the protocol was implemented in this manner, it is deducible the Packets left in the buffer are the Packets that were lost or the packets who’s Acknowledgements were lost.

In order to implement loss detection a number of methods have to be added to the protocol. The basic theory is to remove a Packet from the buffer if the Packet has been acknowledged successfully. This theory seems viable at first but on closer inspection it lacks efficiency. Removing a Packet from the buffer would require copying every other Packet in the old array into a new array, which would be computationally expensive. A slightly different approach was taken which produces the same outcome without the efficiency problem. Instead of removing the acknowledged packet altogether, the packet is marked for deletion. Then when the buffer fills up, a method is called which basically removes the Packets that are market for removal, and copies the remainder into a new buffer. Two implementation issues must be discussed here.

Firstly the Packets are marked for deletion by setting the sequence number of the Packet to –2. Thus any Packets in the buffer that do not have a sequence number of –2 must be retransmitted.

else if(cho->ptype() == PT_RelUDPAck)
{
hdr_RelUDPAck* relUDPAckHdr = hdr_RelUDPAck::access(pkt);
int sequenceNumber =relUDPAckHdr->seqno();
int pIndex=-1;

for(int index=0; index<copy ;index++ )
{
hdr_RelUDP* buffer_relUDPHdr= hdr_RelUDP::access(packetsSent[index]);
if(buffer_relUDPHdr->seqno()==sequenceNumber)
{
pIndex=index;
}
}
if(pIndex!=-1)
{
hdr_RelUDP* old_RelUDPHdr = hdr_RelUDP::access(packetsSent[pIndex]);
old_RelUDPHdr->seqno()=-2;
}
Packet::free(pkt);
}


Figure 23 Revised RelUDP recv() method
Figure 23 shows the rest of the recv method, which deals with receiving acknowledgements. The first loop simply searches through the buffer for a matching Packet with the same sequence number. Then if a Packet is found it is marked for deletion by simply setting its sequence number to –2, as explained previously.

Secondly the implementation of the method that removes the Acknowledged Packets must be explained.

void RelUDPAgent::removeAckdPackets(int newBufferSize)
{
Packet *tempPacketarray = new Packet[newBufferSize];

int numberPacketsAdded=0;
for(int index=0; index<packetIndex_;index++ )
{
hdr_RelUDP* relUDPHdr = hdr_RelUDP::access(packetsSent[index]);
if(relUDPHdr->seqno()!= -2)
{ tempPacketarray[numberPacketsAdded]=*packetsSent[index];
numberPacketsAdded++;
}
}

for(int index=0; index<numberPacketsAdded ;index++ )
{
*packetsSent[index]=tempPacketarray[index];
}

delete[] tempPacketarray;
packetIndex_=numberPacketsAdded;
BufferSize=newBufferSize;
}


Figure 24 RelUDP removeAckdPackets() method

Figure 24 shows the implementation of the removeAckdPackets() method. This method takes in a parameter newBufferSize, which is simply the size of the new buffer that is going to be produced by this method. This new buffer will contain all the packets in the previous buffer that weren’t marked for removal/deletion. The method first creates a temporary Packet array of newBufferSize size, then it simply searches through the original buffer for any Packets who’s sequence number is not –2, or in other words who have not been marked for removal. If it does find such Packets, it copies them into the temporary array. It then copies the Packets from the temporary array into the new buffer array, and deletes the temporary array. This method is quite efficient, as it gives the opportunity of resizing the buffer array only when necessary, thus keeping memory consumption at a minimum.

Therefore the protocol now is capable of detecting Packet loss. The next issue to deal with is Loss Recovery, which basically will implement functionality to recover form the lost Packet, or retransmit the Packet in other words.

  Loss Recovery  

 

As the protocol can now detect lost Packets a method is needed to retransmit these Packets.

void RelUDPAgent::checkBufferLoop()
{
for(int index=0; index<packetIndex_ -4;index++ )
{
hdr_RelUDP* origRelUDPHdr = hdr_RelUDP::access(packetsSent[index]);
//if the packet is not marked for deletion already
if(origRelUDPHdr->seqno()!= -2)
{
Packet *p;
p = allocpkt();
p=packetsSent[index]->copy();
hdr_RelUDP* newRelUDPHdr = hdr_RelUDP::access(p);
recordPacket(p);
origRelUDPHdr->seqno()=-2;
Scheduler::instance().schedule(target_, p, 0);
}
}
}



Figure 25 RelUDP checkBufferLoop()

Figure 25 shows the implementation of the method checkBufferLoop() which is used to retransmit lost Packets. The method simply searches through the buffer for any packets that are not marked for removal, and resends the packet. When it resends the packet it sets the original packet in the buffer for removal, and records a new packet in the buffer. There are two important things to note about this implementation. The method does not search through the last four elements in the array, as they are most likely the packets that are currently being sent (an improved version will be discussed later). Secondly the main issue is from where to call this method. It should be called regularly to ensure that lost packets would be retransmitted quickly. The reliability of the protocol is effected depending on from where this method is called. If it is called form the
 sendmsg() method then there is a possibility that a Packet could get lost after this method was called for the last time in the simulation. For example is a Packet was sent, and the checkBufferLoop() method resends any Packets needed at that time but if the Packet gets lost after that it will never be resent. This would make the protocol unreliable.
 recv() method a similar problem would arise. If the last packet being sent got lost in the Network, the final recv() method would never be called because no Acknowledgement would have been sent. Thus this Packet would never be resent, which also transforms the protocol to an unreliable one.
Therefore it is clear that a new mechanism is needed so that this method can be called regularly to ensure reliability. Luckily the Network Simulator provides a TimeHandler class which solves this problem.

In the Network Simulator Timers may be implemented in C++ or OTcl. The new protocol will need a timer implemented in C++, which is based on an abstract base class (defined in timer-handler.h). The TimerHandler gives the ability to create, schedule, reschedule or cancel a timer. When a timer is scheduled (by using the sched(delay) method) to expire delay seconds in the future. When the timer expires an event is created, which can be handled. Then handling of this event can call the checkBufferLoop() method. This will ensure that the method is called regularly, thus making the protocol reliable.


class CheckBufferTimer : public TimerHandler {
public:
CheckBufferTimer(RelUDPAgent* t) : TimerHandler(), t_(t) {}
inline virtual void expire(Event*);
protected:
RelUDPAgent* t_;
};


Figure 26 Defination of the CheckBufferTimer() method

Figure 26 shows the definition of the timer used in the new protocol. The method void CheckBufferTimer::expire(Event*) will be called when the timer expires. The timer must be rescheduled at the end of the checkBufferLoop() method by the code timername.resched(next_time).
At this point the protocol can be stated to be reliable. The protocol sends Datagram packets, replies with Acknowledgement packets, records the packets it sends, and resends the packets that do not get acknowledged. There is still the issue of retransmission of delayed packets. This is discussed in the next section.

Enforcing a maximum Time Delay

The importance of Packets arriving below a certain delay is discussed in detail in the last chapter. The basic reason is that if the packets are arriving too late, the information might be out of date, and consequently disrupt the performance of multimedia applications. The new protocol is designed with aim of using it in multimedia applications and related areas, therefore it is important to enforce this in the new protocol. The next goal is to implement added functionality to the existing protocol that would retransmit packets if their RTT (Round Trip Time) is greater than a fixed variable or if they have been in the buffer longer than a fixed variable. To recap the Round Trip Time is the amount of time taken between the sending of a Packet and the receiving of an Acknowledgement for that Packet. A number of adjustments need to be made to the protocol to implement this.
Firstly the buffer must be extended.
Figure 27 RelUDP Protocol Buffer
-

As Figure 27 illustrates, the concept of the buffer must hold a packet array that holds the actual packets, and a corresponding integer array that holds the time that the corresponding Packet was sent at.
The next adjustment to the existing protocol is to the methods recordPacket() and removeAckdPackets(). The changes to these methods are as expected and can be seen in the final version of the protocol. The method which requires substantial change would be the checkBufferLoop()


void RelUDPAgent::checkBufferLoop()
{
double sendtime;
double local_time = Scheduler::instance().clock();
for(int index=0; index<packetIndex_;index++ )
{
hdr_RelUDP* origRelUDPHdr = hdr_RelUDP::access(packetsSent[index]);
if(origRelUDPHdr->seqno()!= -2)
{
if((local_time - timesSent[index])>=MAXDELAY)
{
sendtime = Scheduler::instance().clock();
Packet *p;
p = allocpkt();
p=packetsSent[index]->copy();
hdr_RelUDP* newRelUDPHdr = hdr_RelUDP::access(p);
recordPacket(p,sendtime);
origRelUDPHdr->seqno()=-2;
Scheduler::instance().schedule(target_, p, senddelay);
}
}
}
double next_time =CHECK_BUFFER_INT;
if(next_time >0)
{
cb_timer.resched(next_time);
}
}


Figure 28 Revised RelUDP checkBufferLoop() method

Figure 28 shows the changes made to the method checkBufferLoop. Note the timer code is also shown. This method now searches through the buffer for packets that have not been marked for removal and that have not been in the buffer greater than a variable (MAXDELAY) amount of time. If it finds any packets that satisfy these requirements, it resends those packets.

  Duplicate Acknowledgements  

 

This is a problem that can occur in the Protocol design as it is currently designed. The basic problem is that at any time it is possible to have duplicate acknowledgement Packets (acknowledging the same Packet) in the Network.

-
Figure 29 Duplicate Acknowledgement problem

This happens as follows, (see Figure 29) first when a Packet (sequence number 1) is sent by the Protocol and is successfully delivered at its destination, an acknowledgement Packet (sequence number 1) is sent in response. If the network is congested and the acknowledgement has not returned in the MAXDELAY amount of time, the protocol resends Packet 1 as it believes the Packet was lost or delayed in the network. Shortly afterwards the delayed acknowledgement arrives, consequently the protocol believes this to be the acknowledgement of the second retransmitted Packet. Therefore the Protocol will mark a delayed Packet off the buffer that should have been retransmitted.

-

Figure 30 Revised RelUDP Packet Structure

To overcome this problem, a secondary sequence number is needed in both the RelUDP and the RelUDPAck Packet headers. This will make it possible for the Protocol to differentiate between different retransmissions of the same packet. Figure 30 shows the revised RelUDP/RelUDPAck Packet header.

Figure 31 Overcoming Duplicate Acknowledgements with revised RelUDP header.

-

Figure 31 shows how the new header field overcomes this problem. The protocol is able to differentiate between different retransmissions of the same packet.

struct hdr_RelUDP {
u_int32_t srcid_;
int seqno_;
int retryCount_;

/* per-field member functions */
u_int32_t& srcid() { return (srcid_); }
int& seqno() { return (seqno_); }
int& retryCount() { return (retryCount_); }

/* Packet header access functions */



Figure 32 Modifications made to RelUDP header

Figure 32 illustrates the modifications made to the header field of the RelUDP packet header. Note the same changes are needed in the RelUDPAck header. The secondary sequence number variable is called retryCount_. A member function is also implemented to access this variable.
Slight modifications must be made to the Protocol to implement this new secondary sequence number scheme. When a Packet is retransmitted, the new recorded Packet’s retryCount_ Header field is incremented. Also when an acknowledgement arrives it must satisfy all three conditions to remove the corresponding Packet form the buffer/to prove that the Packet has arrived on time.
1. The acknowledgements sequence number must be the same as the corresponding Packets sequence number in the buffer.
2. The acknowledgement’s retryCount_ value must be the same as the corresponding Packet’s retryCount_ value in the buffer.
3. The Packets RTT must be below the MAXDELAY variable time.

 

  Retransmission Design issues  

When implementing the extra functionality on to the Protocol, one final design issue remains. This issue arises with the option of Packet retransmission. Two different designs were created which retransmit Packets at different times and through different methods.
The first design will be discussed here, and the second design is the design chosen for the final end version of the Protocol. The fundamental difference between the two designs is basically when to retransmit lost or delayed packets.

-

Figure 33 Functionality of the RelUDP Protocol Version 1

The first version employs the functionality shown in figure 33. First when a packet is sent by the protocol that arrives successfully at the destination an acknowledgement is sent in return. The main difference is what the protocol does when an acknowledgement is received. It first checks through the buffer for a Packet matching the sequence number and the retryCount value. If it finds such a Packet, it checks the Packets Round Trip Time to be less than the MAXDELAY static variable.
 If it less than the MAXDELAY value is it sets that packet for removal in the buffer
 If it is not less than the MAXDELAY value it simply leaves it up to the checkBufferLoop() method to retransmit the packet. This method is called continuously by the timer ever 25miliseconds.
The checkBufferLoop() method checks through the buffer for Packets that have not been marked for removal and that have been in the buffer greater than MAXDELAY amount of time. Finding any Packets that satisfy these conditions, the method will send a copy of the Packet, record it in the buffer and finally set the original packet for removal.
The main reasons against this version were both logical and physical ones. Firstly the new Protocol should retransmit lost/delayed Packets as quickly as possible, to avoid the performance of the application using the protocol to be hindered. Therefore when the acknowledgement Packet is received, it should retransmit the Packet then rather than leaving it for the checkBufferLoop method to retransmit the Packet later. Another point is that this protocol will retransmit Packets in groups which is generally undesirable as it could possibly worsen congestion in the Network.
The physical issues concern the Network simulator itself. When testing this version of the protocol under highly congested networks, it was found that as the checkBufferLoop method is running at the same time as the protocol. This was seen to produce undesirable results or segmentation faults as the two processes were accessing the same information, for example Packets in the buffer.
The final version of the Protocol deals with these issues and is discussed later in this chapter.

  Hol blocking  

 

The protocol provides reliability, but does not enforce in order delivery. By avoiding this functionality, HOL blocking is avoided.

  No congestion control  

 

As the new protocol is aimed for VoIP signalling or possibly multimedia applications congestion control would be counterproductive. Thus no congestion control was be implemented in the new protocol. Thus if the network does get highly congested, the protocol does not adjust in any way to deal with the congestion; it simply keeps sending Datagram at the same rate.

  Final Version  

 

The protocol was designed from a series of stages, each stage adding new functionality bring the Protocol closer to the final result. As quite a lot of the details have already been discussed in previous stages, this section will give a brief overview of the workings of the Final version of the Protocol explaining any changes as they appear.

-

 

Figure 34 Functionality of the RelUDP Protocol Final Version

Figure 34 shows the functionality and the workings of the Protocol. There are two basic improvements that the Final version of the Protocol makes.


If ( delay >= MAXDELAY )
{
double local_time = Scheduler::instance().clock();
hdr_RelUDP* old_RelUDPHdr = hdr_RelUDP::access(packetsSent[pIndex]);
Packet *newpkt;
newpkt = allocpkt();
newpkt=packetsSent[pIndex]->copy();
hdr_RelUDP* newRelUDPHdr = hdr_RelUDP::access(newpkt);
newRelUDPHdr->retryCount()=(relUDPAckHdr->retryCount()) +1;
target_->recv(newpkt);
recordPacket(newpkt,local_time);
old_RelUDPHdr->seqno()=-3;
}
else
{
hdr_RelUDP* old_RelUDPHdr = hdr_RelUDP::access(packetsSent[pIndex]);
old_RelUDPHdr->seqno()=-2;
}
checkBufferLoop();


Figure 35 Additions to recv() method

The first is what the Protocol does when an acknowledgement is received. Unlike the previous version of the Protocol this version retransmits the Packet as soon as it figures out that it is delayed. Figure 35 illustrates this point. It shows a fragment of the code from the recv() method.
The second improvement is that the timer does not call checkBufferLoop every 25miliseconds. Instead it is called in the receive method, which eliminates the problem of segmentation faults. Every time the method is called it reschedules itself to be called 600 milliseconds later. This in fact only calls the method when the Protocol has finished sending Packets. Thus providing guaranteed reliability, even to the last packet sent.

  Potential Additions  


Including a Random Element

One possible addition that could be added to the Protocol would be to change the static variable MAXDELAY to a random variable between 450 and 500 milliseconds. When a node (router) or link fails, quite a large amount of packets are lost or delayed. This in turn causes these packets to be resent at the same time, which congests the network even more so. Adding this random element will lessen the effect of this problem.

Including Fragmentation

The protocol as it is does not implement fragmentation. Fragmentation is simply a process to overcome the problem of heterogeneous Maximum Transmission Units. Nodes on the Network store the Packets/Datagrams to be sent in memory; when sending the Datagram it is put in an IP frame suitable for the network. Each hardware technology specifies the maximum amount of data that a frame can carry. For example some links on the Network will only be able to transfer a certain amount of data; this is called the Maximum Transmission Unit (MTU).

-

Figure 36 Fragmentation

The problem occurs when a Datagram that is larger than the MTU of the network is sent over the network; the solution is that the Datagram is divided into smaller “fragments” which are each sent separately. This is called Fragmentation. Figure 36 illustrates this.


U.C.C - Computer Science
Click on the more button to visit the University College Cork - Computer Science homepage
 
© 2001-2008 Created by Adrian Duffy