How to write a traffic shaper?

 

[Background]

1.      Please refer to http://en.wikipedia.org/wiki/Token_bucket to know what the token bucket is.

2.      Please refer to http://csie.nqu.edu.tw/smallko/nctuns/sfifo.htm to know how to insert a new module into NCTUNS.

3.      In this example, I refer to http://www.cin.ufpe.br/~cak/ns/ to write a modified version of traffic shaper for NCTUNS.

 

[Prerequisite]

1.    Change the path to /root/NCTUns-6.0/src/nctuns/module/ps

2.      Create a new folder named sFIFO_shaper

3.      Prepare three files under sFIFO_shaper.

(sfifo_shaper.h)

#ifndef __NCTUNS_sfifo_shaper_h__

#define __NCTUNS_sfifo_shaper_h__

 

#include <object.h>

#include <packet.h>

#include <timer.h>

 

/* Define Interface Queue for every Interface */

struct ifqueue {

        ePacket_               *ifq_head; /* head of ifq */

        ePacket_               *ifq_tail; /* tail of ifq */

        int                   ifq_len;   /* current queue length */

        int                   ifq_maxlen;/* max queue length */

        int                   ifq_drops; /* drops count */

};

 

 

/* Define Macros for IFq */

#define IF_QFULL(ifq)           ((ifq)->ifq_len >= (ifq)->ifq_maxlen)

#define IF_DROP(ifq)            ((ifq)->ifq_drops++)

#define IF_ENQUEUE(ifq, m) { \

        if ((ifq)->ifq_tail == 0) \

                (ifq)->ifq_head = m; \

        else \

                (ifq)->ifq_tail->next_ep = m; \

        (ifq)->ifq_tail = m; \

        (ifq)->ifq_len++; \

}

#define IF_PREPEND(ifq, m) { \

        (m)->next_ep = (ifq)->ifq_head; \

        if ((ifq)->ifq_tail == 0) \

                (ifq)->ifq_tail = (m); \

        (ifq)->ifq_head = (m); \

        (ifq)->ifq_len++; \

}

#define IF_DEQUEUE(ifq, m) { \

        (m) = (ifq)->ifq_head; \

        if (m) { \

                if (((ifq)->ifq_head = (m)->next_ep) == 0) \

                        (ifq)->ifq_tail = 0; \

                (m)->next_ep = 0; \

                (ifq)->ifq_len--; \

        } \

}

 

 

class sfifo_shaper : public NslObject {

 

 private:

 

        struct ifqueue             if_snd; /* output interface queue */

        double                  peak_;

        int               burst_size_;

        int                 curr_bucket_contents;

        double                        last_time;

 

        void                     update_bucket_contents();

        int                   in_profile(Packet *);

 

 protected:

       int                  intrq(MBinder *);

 

 public:

 

        sfifo_shaper(u_int32_t type, u_int32_t id, struct plist* pl, const char *name);

        ~sfifo_shaper();

 

        int                   init();

        int                   recv(ePacket_ *);

        int                   send(ePacket_ *);

        int                 get_current_qlen();

};

 

 

#endif /* __NCTUNS_sfifo_shaper_h__ */

 

 

(sfifo_shaper.cc)

#include <stdio.h>

#include <stdlib.h>

#include <assert.h>

#include <fcntl.h>

#include <unistd.h>

#include <sys/stat.h>

#include <nctuns_api.h>

#include <ps/sFIFO_shaper/sfifo_shaper.h>

#include <exportStr.h>

#include <mbinder.h>

 

MODULE_GENERATOR(sfifo_shaper);

 

sfifo_shaper::sfifo_shaper(u_int32_t type, u_int32_t id, struct plist* pl, const char *name)

                : NslObject(type, id, pl, name)

{

        /* disable flow control */

        s_flowctl = DISABLED;

        r_flowctl = DISABLED;

 

        /* initialize interface queue */

        if_snd.ifq_head = if_snd.ifq_tail = 0;

        if_snd.ifq_len = 0;

        if_snd.ifq_drops = 0;

 

        /* bind variable */

        vBind("max_qlen", &if_snd.ifq_maxlen);

    vBind("peak", &peak_);

    vBind("burst_size", &burst_size_);

 

        if_snd.ifq_len = 0;

        if_snd.ifq_maxlen = 50;/* by default */

    peak_= 4000000; // in bps ; default value

    burst_size_ = curr_bucket_contents = 16000; //in bits ; default value

    last_time = 0;

}

 

sfifo_shaper::~sfifo_shaper() {

 

}

 

 

int sfifo_shaper::init() {

 

  int       (NslObject::*upcall)(MBinder *);

 

  /* set upcall */

  upcall = (int (NslObject::*)(MBinder *))&sfifo_shaper::intrq;

  sendtarget_->set_upcall(this, upcall);

  return(1);

}

 

int sfifo_shaper::send(ePacket_ *pkt) {

 

        Packet     *p1=NULL, *p2=NULL;

        ePacket_ *mypkt=NULL;

        int flag1=1, flag2=1;

       

        assert(pkt&&pkt->DataInfo_);

 

        while (flag1 && flag2) {

          if (if_snd.ifq_head!=NULL) {

            GET_PKT(p1, if_snd.ifq_head);

            if(in_profile(p1)) {

              if(!sendtarget_->qfull()) {

                IF_DEQUEUE(&if_snd, mypkt);

              } else {

                flag2=0; // in profile, but sendtarget_ is full.

              }

            } else {

               flag1=0;  // out of profile.

            }

           

            if (mypkt != NULL && flag1 ==1 && flag2==1) {

               NslObject::send(mypkt);

            }

          } else {

              break;

          }

        } 

 

        if(flag1 == 1 && flag2 ==1){

          GET_PKT(p2, pkt);

          if(in_profile(p2)) {

             if(!sendtarget_->qfull()) {

                 return(NslObject::send(pkt));

             } else {

                 IF_ENQUEUE(&if_snd, pkt);

                 return(1);

             }

          } else {

              IF_ENQUEUE(&if_snd, pkt);

              return(1);

          }       

        }

 

        if(flag1==0) {

             if (IF_QFULL(&if_snd)){

               IF_DROP(&if_snd);

               freePacket(pkt);

               return(1);

             }

             IF_ENQUEUE(&if_snd, pkt);

             return(1);

        }

       

        if(flag2==0) {

             if (IF_QFULL(&if_snd)){

               IF_DROP(&if_snd);

               freePacket(pkt);

               return(1);

             }

             IF_ENQUEUE(&if_snd, pkt);

             return(1);

        }

       

        printf("something wrong here, flag1=%d flag2=%d\n", flag1, flag2);

        return(1);

}

 

 

int sfifo_shaper::recv(ePacket_ *pkt) {

 

        /* Just by pass incoming packet */

        assert(pkt&&pkt->DataInfo_);

        return(NslObject::recv(pkt));

}

 

 

int sfifo_shaper::intrq(MBinder *port) {

 

        ePacket_  *pkt=NULL;

        Packet              *p;

      

        if (if_snd.ifq_len!=0) {

           GET_PKT(p, if_snd.ifq_head);

           if(in_profile(p)){

             IF_DEQUEUE(&if_snd, pkt);

           }

        } 

     

        if (pkt != NULL) {

                /*

                 * If still exist packet in the interface

                 * queue, we try to push it to the MBQ,

                 */

                assert(sendtarget_->enqueue(pkt) == 0);

        }

        return(1);

}

 

 

int sfifo_shaper::in_profile(Packet *p){

 

    update_bucket_contents();

 

    int packetsize = (p->pkt_getlen()-42) * 8;

   

    //printf("in_profile(), packetsize=%d, curr_bucket_contents=%d\n", packetsize, curr_bucket_contents);

    if(packetsize > curr_bucket_contents)

      return 0;

    else {

      curr_bucket_contents -= packetsize;

      return 1;

    }

}

 

 

void sfifo_shaper::update_bucket_contents(){

     double current_time = GetCurrentTime() * TICK / 1000000000.0;

     double added_bits = (current_time - last_time) * peak_ ;

     //printf("current_time=%f last_time=%f, added_bits=%f\n", current_time, last_time, added_bits);

     curr_bucket_contents+=(int)(added_bits + 0.5);

     if(curr_bucket_contents > burst_size_)

        curr_bucket_contents = burst_size_;

     last_time = current_time;

}

 

 

(Makefile)

#

# Makefile for the NCTUns engine module

#

 

obj-y        = sfifo_shaper.o

 

 

4.      Change path to /root/NCTUns-6.0/src/nctuns and edit the nctuns.cc

Add the following in this file. 

REG_MODULE(sFIFO_shaper”, sfifo_shaper);

 

5.      Recompile the nctuns.

 

 

[Simulation]

1.      Create a topology.

 

 

2.      Click ‘E’ to edit property.

 

 

3.      Click ‘R’ and Simulation/Run to start simulation. (without traffic shaper).

4.      During the simulation, see the window that runs the “coordinator” command and you will see.

Without traffic shaping, the throughput in Node 2 is around 630 Kbyte/sec.

 

5.      Now, click “R” again. But don’t to click Simulation/Run. We have to modify the tcl file first.

 

 

  

6.      Click Simulation/Run to start simulation again (with traffic shaping). We can see

We can see the throughput is 250 Kbyte/sec (equal to 250*8=2000Kbits/sec). That is what we set in the tcl file (Set Node1_sFIFO_shaper_LINK_1.peak = 2000000).

 

Dr. Chih-Heng Ke (http://csie.nqu.edu.tw/smallko), smallko@gmail.com

Department of Computer Science and Information Engineering, National Quemoy University, Taiwan