Hi everyone,

because i am having problems with Nsclick simulations that take forever to complete, i have been looking at possible causes. I've identified a flaw in the scheduling. Everytime the driver() function of routerthread.cc is executed (which is caused by timer expiration or packet reception), it schedules itself again in NS-2 if it has a timer that is scheduled to go off in the future. As a result, many duplicate schedules are introduced for the same node for exactly the same point in simulation time. This introduces great overhead when this point in simulation time is reached and all these duplicate events have to be executed.

I've found a very old post on the mailing list by Michael Neufeld (http://pdos.csail.mit.edu/pipermail/click/2004-May/002706.html) which presented a hack to overcome this problem. Basically he keeps a global map of points in time for which a ClickEvent is scheduled. If a node schedules a new event, it will be added to the unique already existing event for that given moment in time. During this addition, a check is performed to make sure that you schedule a node only once for a single point in time.

This code however was never introduced into mainstream click. I have implemented a new version which should be compatible with recent Click releases. The files can be found in attachment. They have to be put in the classifier directory of the NS-2 sources.

Perhaps it would be interesting if some people using NSClick in NS-2 could try these files, and see if they also observe an improvement in performance, and if it does not introduce any bias in the simulation results? Also, i have tested it with Click 1.7 (upgrading is still on my to-do list) and NS2.34, experience with current git sources would also be interesting.

Regards,

Wim


PS: I also noticed that using a QuickNoteQueue instead of a standard Queue improves performance since the run_task of the tosimdevice will halt quicker when there are
no more packets left in the queue

--
Wim Vandenberghe
Department of Information Technology
Internet Based Communication Networks and Services (IBCN)
Ghent University - IBBT
Gaston Crommenlaan 8 (Bus 201), B-9050 Gent, Belgium
T: +32 9 33 14946 ; T Secr: +32 9 33 14900
F: +32 9 33 14899
E: [email protected]
W : www.ibcn.intec.UGent.be

/*
 * classifier-click classifier file for nsclick
 */

/*****************************************************************************
 *  Copyright 2002, Univerity of Colorado at Boulder.                        *
 *                                                                           *
 *                        All Rights Reserved                                *
 *                                                                           *
 *  Permission to use, copy, modify, and distribute this software and its    *
 *  documentation for any purpose other than its incorporation into a        *
 *  commercial product is hereby granted without fee, provided that the      *
 *  above copyright notice appear in all copies and that both that           *
 *  copyright notice and this permission notice appear in supporting         *
 *  documentation, and that the name of the University not be used in        *
 *  advertising or publicity pertaining to distribution of the software      *
 *  without specific, written prior permission.                              *
 *                                                                           *
 *  UNIVERSITY OF COLORADO DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS      *
 *  SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND        *
 *  FITNESS FOR ANY PARTICULAR PURPOSE.  IN NO EVENT SHALL THE UNIVERSITY    *
 *  OF COLORADO BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL         *
 *  DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA       *
 *  OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER        *
 *  TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR         *
 *  PERFORMANCE OF THIS SOFTWARE.                                            *
 *                                                                           *
 ****************************************************************************/
/*
 *      Modified by Nicolas Letor to support wifi elements.
 *      Performance Analysis of Telecommunication Systems (PATS) research group,
 *      Interdisciplinary Institute for Broadband Technology (IBBT) & 
Universiteit Antwerpen.
 *
 *      Modified by Wim Vandenberghe to improve performance
 *      Based on an old post by Michael Neufeld on the click mailinglist 
(http://pdos.csail.mit.edu/pipermail/click/2004-May/002706.html)
 *      Internet Based Communication Networks and Services (IBCN) research 
group,
 *      Interdisciplinary Institute for Broadband Technology (IBBT) & 
Universiteit Gent.
 */
#include "config.h"
#include <stdlib.h>
#include <ctype.h>
#include <sys/time.h>
#include <unistd.h>
//#include <stl.h>
//#include <hash_map.h>
#include <map>
#include <string>
#include <stdarg.h>
#include <algorithm>

#include "agent.h"
#include "packet.h"
#include "rawpacket.h"
#include "ip.h"
#include "extrouter.h"
#include "classifier.h"
#include "classifier-ext.h"
#include "mobilenode.h"
#include "clicknode.h"
#include "address.h"
#include <click/simclick.h>
#include "scheduler.h"
#include "classifier-click.h"
#include "ll-ext.h"
#include "clickqueue.h"



#include "packet_anno.h"

static class ClickClassifierClass : public TclClass {
public:
  ClickClassifierClass() : TclClass("Classifier/Ext/Click") {}
  TclObject* create(int, const char*const*) {
    return (new ClickClassifier());
  }
} class_click_classifier;


void
ClickEventHandler::handle(Event* event) {
    // XXX dangerous downcast - should use RTTI
    // XXX multithreading!
    ClickEvent* cevent = (ClickEvent*) event;
    //run all other click instances which where attached to this event using 
the clickinst_ list of the event
    while(!cevent->clickinst_.empty()){
        simclick_node_t * scheduledNode = cevent->clickinst_.front();
        scheduledNode->curtime=cevent->when_;
        simclick_click_run(scheduledNode);
        cevent->clickinst_.pop_front();
    }
    ClickClassifier::global_clickevents_.erase(cevent->when_);
    delete cevent;
}

map<MACAddr,int> ClickClassifier::global_mactonodemap_;
map<MACAddr,int> ClickClassifier::global_mactonsmacmap_;
map<u_int32_t,int> ClickClassifier::global_ipmap_;
map<struct timeval,ClickEvent*> ClickClassifier::global_clickevents_;


ClickClassifier::ClickClassifier() : rate_(-1), tx_power_(-1) {
  extrouter_ = this;
  click_initialized_ = false;

}

int
ClickClassifier::command(int argc, const char*const* argv)
{
  Tcl& tcl = Tcl::instance();
  if (2 == argc) {
    if (strcmp(argv[1], "getnodename") == 0) {
      // getnodename
      tcl.resultf(nodename_.c_str());
      return TCL_OK;
    }
    if (strcmp(argv[1], "runclick") == 0) {
      // runclick
      if (click_initialized_) {
          simclick_node_t::curtime = GetSimTime();
          simclick_click_run(this);
      }
      return TCL_OK;
    }
  }
  else if (3 == argc) {
    if(strcmp(argv[1], "loadclick") == 0) {
        simclick_node_t::curtime = GetSimTime();
        if (simclick_click_create(this, argv[2]) >= 0) {
            click_initialized_ = true;
            simclick_click_run(this);
        }

      return TCL_OK;
    }
    if (strcmp(argv[1], "getip") == 0) {
      // getip <ifname>
      int theif = GetIFID(argv[2]);
      //fprintf(stderr,"get ipaddr is %s\n",ifipaddrs_[theif].c_str());
      tcl.resultf(ifipaddrs_[theif].c_str());
      return TCL_OK;
    }
    if (strcmp(argv[1], "getmac") == 0) {
      // getmac <ifname>
      int theif = GetIFID(argv[2]);
      //fprintf(stderr,"get macaddr is %s\n",ifmacaddrs_[theif].c_str());
      tcl.resultf(ifmacaddrs_[theif].c_str());
      return TCL_OK;
    }
    if (strcmp(argv[1], "setnodename") == 0) {
      // setnodename <ifname>
      nodename_ = argv[2];
      return TCL_OK;
    }
    if (strcmp(argv[1], "setnodeaddr") == 0) {
      // setnodeaddr <nodeaddress>
      nodeaddr_ =  Address::instance().str2addr(argv[2]);
      return TCL_OK;
    }
    //mvhaen -- meant to set the trace file
    if (strcmp(argv[1], "tracetarget") == 0) {
      logtarget_ = ( CMUTrace* ) TclObject::lookup(argv[2]);
      if (logtarget_ == 0)
          return TCL_ERROR;
      return TCL_OK;
    }
  }
  else if (4 == argc) {
    if(strcmp(argv[1], "setip") == 0) {
      // setip <ifname> <ipaddr>
      int theif = GetIFID(argv[2]);
      ifipaddrs_[theif] = string(argv[3]);
      //fprintf(stderr,"ipaddr is %s\n",ifipaddrs_[theif].c_str());
      // Also save the binary form of this IP address in a static
      // (i.e. simulator global) hash map of IP addresses to ns-2
      // addresses. This lets us track map IP to ns-2 address when
      // we might need it.
      global_ipmap_[inet_addr(argv[3])] = nodeaddr_;
      return TCL_OK;
    }
    else if(strcmp(argv[1], "setmac") == 0) {
      // setmac <ifname> <macaddr>
      int theif = GetIFID(argv[2]);
      ifmacaddrs_[theif] = string(argv[3]);

      //fprintf(stderr,"macaddr is %s\n",ifmacaddrs_[theif].c_str());

      // Also save the binary form of this MAC address in a static
      // (i.e. simulator global) hash map of MAC addresses to ns-2
      // addresses. This lets us set the destination address in the
      // ns-2 packet header.
      MACAddr thismacaddr = MACAddr(string(argv[3]));
      global_mactonodemap_[thismacaddr] = nodeaddr_;
      LL* mylink = (LL*) slot_[theif];
      global_mactonsmacmap_[thismacaddr] = mylink->macDA();
      return TCL_OK;
    }
    else if (strcmp(argv[1], "readhandler") == 0) {
      char* readreturn = 0;
      simclick_node_t::curtime = GetSimTime();
      readreturn = simclick_click_read_handler(this,argv[2],argv[3],0,0);
      //fprintf(stderr, "readhandler: %s\n",clickretc);
      if (readreturn) {
        tcl.resultf("%s", readreturn);
        free(readreturn);
        readreturn = 0;
      }
      else {
        tcl.resultf("");
      }
      return TCL_OK;
    }
  } else if (argc == 5) {
    if (strcmp(argv[1], "writehandler") == 0) {
      int clickret;
      simclick_node_t::curtime = GetSimTime();
      clickret = simclick_click_write_handler(this, argv[2], argv[3], argv[4]);
      //fprintf(stderr, "writehandler: %i\n",clickret);
      tcl.resultf("%i", clickret);
      return TCL_OK;
    }
  }

  return ExtClassifier::command(argc, argv);
}


ClickClassifier::~ClickClassifier() {
}

int
ClickClassifier::route(Packet* p) {
  int result = 0;
  if (click_initialized_) {
    unsigned char* data = NULL;
    int len = ((PacketData*)(p->userdata()))->size();
    simclick_simpacketinfo simpinfo;
    hdr_cmn* chdr = HDR_CMN(p);
    int ifid = chdr->iface_;
    hdr_ip* iphdr = hdr_ip::access(p);
    simpinfo.id = chdr->uid();
    simpinfo.fid = iphdr->flowid();
    hdr_raw* rhdr = hdr_raw::access(p);
    int nssubtype = rhdr->subtype;
    int clicktype = GetClickPacketType(nssubtype);
    simpinfo.simtype = rhdr->ns_type;
    unsigned char* pdat = p->accessdata();
    data = new unsigned char[len];
        memcpy(data,pdat,len);
        
    /*
     * XXX Destroy packet for now. This may change if we wind
     * up having to track and reuse ns packets after they've gone through
     * click.
     */
    Packet::free(p);
    p = NULL;

    simclick_node_t::curtime = GetSimTime();
    //fprintf(stderr,"Sending packet up to click...\n");
    simclick_click_send(this,ifid,clicktype,data,len,&simpinfo);
    delete[] data;
    data = 0;
  }
  else {
    fprintf(stderr,"No click upcall set!\n");
  }
  return result;
}

string
ClickClassifier::GetIPAddr(int ifid) {
  return ifipaddrs_[ifid];
}

string
ClickClassifier::GetMACAddr(int ifid) {
  return ifmacaddrs_[ifid];
}

string
ClickClassifier::GetNodeName() {
  return nodename_;
}

int
ClickClassifier::GetNodeAddr()
{
  return nodeaddr_;
}

int
ClickClassifier::GetIFID(const char *ifname) const
{
    int r = -1;

    /*
     * Provide a mapping between a textual interface name
     * and the id numbers used. This is mostly for the
     * benefit of click scripts, i.e. you can still refer to
     * an interface as, say, /dev/eth0.
     */
    if (strstr(ifname, "tap") || strstr(ifname, "tun")) {
        /*
         * A tapX or tunX interface goes to and from the kernel -
         * always IFID_KERNELTAP
         */
        r = ExtRouter::IFID_KERNELTAP;
    } else if (const char *devname = strstr(ifname, "eth")) {
        /*
         * Anything with an "eth" followed by a number is
         * a regular interface. Add the number to IFID_FIRSTIF
         * to get the handle.
         */
        while (*devname && !isdigit((unsigned char) *devname))
            devname++;
        if (*devname)
            r = atoi(devname) + ExtRouter::IFID_FIRSTIF;
    } else if (const char *devname = strstr(ifname, "drop")) {
        /*
         * Anything with an "drop" followed by a number is
         * a special interface on which we place packets that
         * get dropped due to MAC layer feedback. Add the number to
         * IFID_FIRSTIFDROP to get the handle.
         */
        while (*devname && !isdigit((unsigned char) *devname))
            devname++;
        if (*devname)
            r = atoi(devname) + ExtRouter::IFID_FIRSTIFDROP;
    }

    return r;
}

/*
 * Click service methods
 */
extern "C" {

static int simstrlcpy(char *buf, int len, const string &s) {
    if (len) {
        len--;
        if ((unsigned) len > s.length())
            len = s.length();
        s.copy(buf, len);
        buf[len] = '\0';
    }
    return 0;
}

int simclick_sim_command(simclick_node_t *simnode, int cmd, ...)
{
        ClickClassifier *cc = static_cast<ClickClassifier *>(simnode);
    Tcl &tcl = Tcl::instance();
    va_list val;
    va_start(val, cmd);
    int r;

    switch (cmd) {
        
      case SIMCLICK_VERSION:
        r = 0;
        break;

      case SIMCLICK_SUPPORTS: {
          int othercmd = va_arg(val, int);
          r = othercmd >= 0 && othercmd <= SIMCLICK_CHANGE_CHANNEL;
          break;
      }

      case SIMCLICK_IFID_FROM_NAME: {
          const char *ifname = va_arg(val, const char *);
          r = cc->GetIFID(ifname);
          break;
      }

      case SIMCLICK_IPADDR_FROM_NAME: {
          const char *ifname = va_arg(val, const char *);
          char *buf = va_arg(val, char *);
          int len = va_arg(val, int);
          int ifid = cc->GetIFID(ifname);
          r = simstrlcpy(buf, len, cc->GetIPAddr(ifid));
          break;
      }

      case SIMCLICK_MACADDR_FROM_NAME: {
          const char *ifname = va_arg(val, const char *);
          char *buf = va_arg(val, char *);
          int len = va_arg(val, int);
          int ifid = cc->GetIFID(ifname);
          r = simstrlcpy(buf, len, cc->GetMACAddr(ifid));
          break;
      }

      case SIMCLICK_SCHEDULE: {
          const struct timeval *when = va_arg(val, const struct timeval *);
          ClickEvent *ev = ClickClassifier::global_clickevents_[*when];
          if(!ev){
                ev = new ClickEvent();
                ev->when_ = *when;
                ev->clickinst_.push_back(simnode);
                double simtime = when->tv_sec + (when->tv_usec / 1.0e6);
                double simdelay = simtime - Scheduler::instance().clock();
                ClickClassifier::global_clickevents_[*when] = ev;
                Scheduler::instance().schedule(&cc->cevhandler_, ev, simdelay);
          }
          //if already have event for that time but not yet have this node in 
the node list, add id
          else 
if(find(ev->clickinst_.begin(),ev->clickinst_.end(),simnode)==ev->clickinst_.end())
                          ev->clickinst_.push_back(simnode);

          r = 0;
          break;
      }

      case SIMCLICK_GET_NODE_NAME: {
          char *buf = va_arg(val, char *);
          int len = va_arg(val, int);
          r = simstrlcpy(buf, len, cc->GetNodeName());
          break;
      }

      case SIMCLICK_IF_READY: {
          int ifid = va_arg(val, int);
          r = cc->IFReady(ifid);
          break;
      }

      case SIMCLICK_TRACE: {
          const char *event = va_arg(val, const char *);
          cc->trace("%s", event);
          r = 1;
          break;
      }

      case SIMCLICK_GET_NODE_ID: {
          r = cc->GetNodeAddr();
          break;
      }

      case SIMCLICK_GET_POSITION_X: {
          MobileNode *iNode;
          iNode = (MobileNode *)(Node::get_node_by_address(cc->GetNodeAddr()));
          double xpos = 0.0;
          xpos = iNode->X();
          r=(int) xpos;
          break;
      }

      case SIMCLICK_GET_POSITION_Y: {
                  MobileNode *iNode;
                  iNode = (MobileNode 
*)(Node::get_node_by_address(cc->GetNodeAddr()));
                  double ypos = 0.0;
                  ypos = iNode->Y();
                  r=(int) ypos;
                  break;
            }


      case SIMCLICK_GET_NEXT_PKT_ID:
        r = cc->GetNextPktID();
        break;

      case SIMCLICK_CHANGE_CHANNEL: {
          int ifid = va_arg(val, int);
          int channelid = va_arg(val, int);
          char work[128];
          sprintf(work, "SwitchChannel %i %i %i", cc->GetNodeAddr(), ifid, 
channelid);
          tcl.eval(work);
          r = 0;
          break;
      }

      default:
        r = -1;
        break;
        
    }
    
    va_end(val);
    return r;
}

int
simclick_sim_send(simclick_node_t *simnode,
                  int ifid,int type, const unsigned char* data,int len,
                  simclick_simpacketinfo* pinfo) {

  if (NULL == simnode) {
    return -1;
  }

  /*
   * Bail out if we get a bad ifid
   */
  if (ExtRouter::IFID_LASTIF < ifid) {
    return -1;
  }
  /*
   * XXX should probably use RTTI typesafe casts if they are now
   * reliably implemented across the compilers/platforms we want
   * to run on.
   */
  ClickClassifier* theclassifier = static_cast<ClickClassifier*>(simnode);

  return theclassifier->send_to_if(ifid,type,data,len,pinfo);
}

}

int
ClickClassifier::send_to_if(int ifid,int type,const unsigned char* data,
                         int len,simclick_simpacketinfo* pinfo) {
  int result = 0;

  /*
   * Package raw data into an ns-2 format raw packet, then send
   * it on down the line.
   */

  Packet* pkt = MakeRawPacket(type,ifid,data,len,pinfo);
  //fprintf(stderr,"simclickid == %d\n",simclickid);
  recv(pkt,0);

  return result;
}

int
ClickClassifier::IFReady(int ifid) {
  NsObject* target = NULL;
  int ready = 0;

  // XXX assumes direct ifid->slot mapping
  if (ExtRouter::IFID_KERNELTAP == ifid) {
    return 1;
  }

  target = slot_[ifid];
  if (target) {
    LLExt* llext = (LLExt*) target;
    ready = llext->ready();
  }
  else {
    ready = 0;
    fprintf(stderr,"ERROR: network interface does not exist\n");
  }

  return ready;
}

int
ClickClassifier::GetNSSubtype(int type) {
  switch (type) {
  case SIMCLICK_PTYPE_ETHER:
    return hdr_raw::ETHERNET;

  case SIMCLICK_PTYPE_IP:
    return hdr_raw::IP;

  default:
    return hdr_raw::NONE;
  }

  return hdr_raw::NONE;
}

int
ClickClassifier::GetClickPacketType(int nssubtype) {
  switch (nssubtype) {
  case hdr_raw::ETHERNET:
    return SIMCLICK_PTYPE_ETHER;

  case hdr_raw::IP:
    return SIMCLICK_PTYPE_IP;

  case hdr_raw::MADWIFI:
    return SIMCLICK_PTYPE_ETHER;
  
  default:
    return SIMCLICK_PTYPE_UNKNOWN;
  }

  return SIMCLICK_PTYPE_UNKNOWN;
}

void ClickClassifier::setRate(int rate){
        if (rate>=0 && rate<=3)
                rate_ = rate;
}

void ClickClassifier::setTxPower(double txpower){
        if(txpower>=0)
                tx_power_=txpower;
}

// XXX 
// Normally I'd bitterly complain about code like this. However,
// I don't really want to worry about annoying differences
// between IP header files across different platforms, and I
// want to get this code up and running ASAP. So... I'm defining
// a few things here to handle the minimal packet cracking I
// need to do to create raw packets. If more complicated
// packet munging is called for, something better should be created.
#define NS_ETHER_OFFSET_DADDR 0
#define NS_ETHER_OFFSET_SADDR 6
#define NS_ETHER_HEADER_SIZE 14
#define NS_80211_OFFSET_DADDR 4
#define NS_80211_OFFSET_SADDR 10

void
ClickClassifier::LinkLayerFailedCallback(Packet* p, void* arg) {
  // Hit the callback and then free the packet
  ((ClickClassifier*)arg)->LinkLayerFailed(p);
  Packet::free(p);
}

void
ClickClassifier::LinkLayerFailed(Packet* p) {
  //fprintf(stderr,"XXX Lost a packet!!!\n");
  if (click_initialized_) {
    unsigned char* data = NULL;
    int len = ((PacketData*)(p->userdata()))->size();
    simclick_simpacketinfo simpinfo;
    hdr_cmn* chdr = HDR_CMN(p);
    int ifid = chdr->iface_ + IFID_LASTIF;
    hdr_ip* iphdr = hdr_ip::access(p);
    simpinfo.id = chdr->uid();
    simpinfo.fid = iphdr->flowid();
    hdr_raw* rhdr = hdr_raw::access(p);
    int nssubtype = rhdr->subtype;
    int clicktype = GetClickPacketType(nssubtype);
    unsigned char* pdat = p->accessdata();
    data = new unsigned char[len];
    memcpy(data,pdat,len);
    simclick_node_t::curtime = GetSimTime();
    //fprintf(stderr,"Sending packet up to click...\n");
    simclick_click_send(this,ifid,clicktype,data,len,&simpinfo);
    delete[] data;
    data = 0;
  }
  else {
    fprintf(stderr,"No click upcall set!\n");
  }
}

Packet*
ClickClassifier::MakeRawPacket(int type,int ifid,const unsigned char* data,
                               int len,simclick_simpacketinfo* pinfo){
  Packet* pkt = Packet::alloc(len);
  /*
   * Shovel raw data into packet
   */
  hdr_raw* rhdr = hdr_raw::access(pkt);
  // nletor -- check if it is a wireless packet and threat it accordingly
  click_wifi_extra *ceh = (click_wifi_extra *) data;
  if (ceh->magic == WIFI_EXTRA_MAGIC) {
        rhdr->subtype = hdr_raw::MADWIFI;  
  } else {
  rhdr->subtype = GetNSSubtype(type);
  }
  unsigned char* pdat = pkt->accessdata();
  memcpy(pdat,data,len);

  /*
   * Set some of the packet header stuff ns-2 wants
   */
  struct hdr_cmn* chdr = HDR_CMN(pkt);
  chdr->iface() = ifid;
  chdr->ptype() = PT_RAW;
  chdr->size() = len;
  if (pinfo->id >= 0) {
    chdr->uid() = pinfo->id;
  }
  else {
    chdr->uid() = Agent::getnextuid();
  }
  rhdr->ns_type = (-1 == pinfo->simtype) ? PT_RAW : pinfo->simtype;
  chdr->xmit_failure_ = LinkLayerFailedCallback;
  chdr->xmit_failure_data_ = (void*)this;


  hdr_ip* iphdr = hdr_ip::access(pkt);
  iphdr->flowid() = 0;
  if (pinfo->fid >= 0) {
    iphdr->flowid() = pinfo->fid;
  }

  /*
   * A packet coming in from click on the kernel tap device is
   * considered to be going up into the node, on any other device
   * going down out of it.
   */
  if (ExtRouter::IFID_KERNELTAP == ifid) {
    chdr->direction() = hdr_cmn::UP;
  }
  else {
    chdr->direction() = hdr_cmn::DOWN;
    // Going out to a network adapter, and we're already 
    // ethernet encapsulated. The ns-2 interface code will
    // tack on ethernet header overhead as well, so we subtract
    // it out of our simulated size here to avoid actual packet
    // size inflation
    if (hdr_raw::ETHERNET == rhdr->subtype) {
      chdr->size() -= NS_ETHER_HEADER_SIZE;
    } else if (hdr_raw::MADWIFI == rhdr->subtype) {     
          // nsmadwifi
                chdr->size() -= sizeof(click_wifi_extra);
                chdr->size() -= sizeof(click_wifi);
    }
  }

  // If we've got ethernet encapsulation, translate mac address
  // to ns address. Otherwise we're SOL.
  struct hdr_mac* mhdr = HDR_MAC(pkt);
  if (hdr_raw::ETHERNET == rhdr->subtype) {
    MACAddr dmac(data + NS_ETHER_OFFSET_DADDR);
    MACAddr smac(data + NS_ETHER_OFFSET_SADDR);
    if (dmac.is_broadcast()) {
      mhdr->macDA_ = MAC_BROADCAST;
    }
    else {
      mhdr->macDA_ = global_mactonsmacmap_[dmac];
      //fprintf(stderr,"XXX using real MAC: %s -> 
%d\n",dmac.to_string().c_str(),mhdr->macDA_);
    }
    mhdr->macSA_ = global_mactonsmacmap_[smac];
    chdr->next_hop_ = global_mactonodemap_[dmac];
    chdr->prev_hop_ = global_mactonodemap_[smac];
  } else if (hdr_raw::MADWIFI == rhdr->subtype) {
        //TODO
        MACAddr dmac(data + NS_ETHER_OFFSET_DADDR + sizeof(click_wifi_extra) + 
4); // destination address (STA,AP whatever)
    MACAddr smac(data + NS_ETHER_OFFSET_SADDR + sizeof(click_wifi_extra) + 4); 
// source address (STA,AP whatever)
        if (dmac.is_broadcast()) {
      mhdr->macDA_ = MAC_BROADCAST;
    } else {
      mhdr->macDA_ = global_mactonsmacmap_[dmac];
    }
    mhdr->macSA_ = global_mactonsmacmap_[smac];
    chdr->next_hop_ = global_mactonodemap_[dmac];
    chdr->prev_hop_ = global_mactonodemap_[smac];
          
  } else {
    //fprintf(stderr,"XXX using broadcast mac XXX\n");
    mhdr->macDA_ = MAC_BROADCAST;
  }

  // Got an IP packet? Must have come from click, and therefore
  // the next hop is us.
  if ((ExtRouter::IFID_KERNELTAP == ifid) && (hdr_raw::IP == rhdr->subtype)) {
    chdr->next_hop() = nodeaddr_;
  }

  return pkt;
}

struct timeval
ClickClassifier::GetSimTime() {
  struct timeval curtime;
  double ns2time = Scheduler::instance().clock();
  double fracp,intp;
  fracp = modf(ns2time,&intp);
  curtime.tv_sec = (long) intp;
  curtime.tv_usec = (long) (fracp * 1.0e6 + 0.5); 
  return curtime;
}

void
ClickClassifier::trace(char* fmt, ...)
{
        va_list ap;

        if ( !logtarget_ ) {
                printf( "ClickClassifier: need to configure tracetarget\n" );
                return ;
        }
        va_start( ap, fmt );
        vsprintf( logtarget_->pt_->buffer(), fmt, ap );
        logtarget_->pt_->dump();
        va_end( ap );
}

int
ClickClassifier::GetNextPktID()
{
        return Agent::getnextuid();
}
/*
 *
 * This might not seem like a regular classifier, and it isn't.
 * It essentially has a fixed interface ID which it sends along
 * with its packet to the ClickNode it lives on, the idea being
 * that the Click subsystem will be the thing which actually
 * does the classifying, not the classifier.
 */

/*****************************************************************************
 *  Copyright 2002, Univerity of Colorado at Boulder.                        *
 *                                                                           *
 *                        All Rights Reserved                                *
 *                                                                           *
 *  Permission to use, copy, modify, and distribute this software and its    *
 *  documentation for any purpose other than its incorporation into a        *
 *  commercial product is hereby granted without fee, provided that the      *
 *  above copyright notice appear in all copies and that both that           *
 *  copyright notice and this permission notice appear in supporting         *
 *  documentation, and that the name of the University not be used in        *
 *  advertising or publicity pertaining to distribution of the software      *
 *  without specific, written prior permission.                              *
 *                                                                           *
 *  UNIVERSITY OF COLORADO DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS      *
 *  SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND        *
 *  FITNESS FOR ANY PARTICULAR PURPOSE.  IN NO EVENT SHALL THE UNIVERSITY    *
 *  OF COLORADO BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL         *
 *  DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA       *
 *  OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER        *
 *  TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR         *
 *  PERFORMANCE OF THIS SOFTWARE.                                            *
 *                                                                           *
 ****************************************************************************/

#ifndef ns_classifier_click_h
#define ns_classifier_click_h

#include "object.h"
#include "cmu-trace.h"
#include <deque>

class Packet;


class ClickEvent : public Event {
 public:
  // Store an extra copy of the call time in sec/usec format.
  // This is to sidestep some roundoff errors which occured
  // when going back and forth between sec/usec and doubles.
  struct timeval when_;
  //wimvdber: this is based on an old neufeld nsclick performance patch
  // Keep a list of clickinstances around so we can run all
  // of them with only one scheduled event.
  // XXX may want to turn this into a hash to avoid needlessly running
  // the click graph...
  deque<simclick_node_t *> clickinst_;

};

class ClickEventHandler : public Handler {
 public:
  virtual void handle(Event* event);
};

class MACAddr {
 public:
  MACAddr() {
    memset(macaddr_,0,6);
  }
  explicit MACAddr(const string straddr) {
      unsigned crap[6];
      sscanf(straddr.c_str(), "%02X:%02X:%02X:%02X:%02X:%02X", &crap[0],
             &crap[1], &crap[2], &crap[3], &crap[4], &crap[5]);
      for (int i = 0; i < 6; i++)
          macaddr_[i] = crap[i];
  }  
  explicit MACAddr(const unsigned char* rawaddr) {
    memcpy(macaddr_,rawaddr,6);
  }
  bool operator==(const MACAddr& rhs) const {
    return(0 == memcmp(macaddr_,rhs.macaddr_,6));
  }

  bool is_broadcast() {
    for (int i=0;i<6;i++) {
      if (macaddr_[i] != 0xff) {
        return false;
      }
    }
    return true;
  }

  string to_string() {
    char tmp[64];
    sprintf(tmp, "%02X:%02X:%02X:%02X:%02X:%02X", macaddr_[0],
           macaddr_[1], macaddr_[2], macaddr_[3], macaddr_[4],
           macaddr_[5]);

    return string(tmp);
  }
  unsigned char macaddr_[6];
};

namespace std {
template<>
struct less<MACAddr> {
  bool operator()(const MACAddr& l, const MACAddr& r) const {
    // Treat MAC as a big old integer...
    uint32_t leftu = *((uint32_t*)(l.macaddr_));
    uint32_t rightu = *((uint32_t*)(r.macaddr_));
    uint16_t leftl = *((uint16_t*)(l.macaddr_+4));
    uint16_t rightl = *((uint16_t*)(r.macaddr_+4));
    
    // Check the upper bytes first, if those are equal check lower
    if (leftu < rightu) {
      return true;
    }
    else if (leftu == rightu) {
      return (leftl < rightl);
    }
    return false;
  }
};

template<>
struct less<struct timeval> {
  bool operator()(const struct timeval& l, const struct timeval& r) const {
    // Simple comparison...
    // Check the seconds first, if those are equal check usec
    if (l.tv_sec < r.tv_sec) {
      return true;
    }
    else if (l.tv_sec == r.tv_sec) {
      return (l.tv_usec < r.tv_usec);
    }
    return false;
  }
};

}

class ClickClassifier : public ExtClassifier, public ExtRouter,
    public simclick_node_t {
 public:
  ClickClassifier();
  virtual ~ClickClassifier();
  virtual int command(int argc, const char*const* argv);
  
  /*
   * Stuff to handle click requests
   */
 public:
  virtual int send_to_if(int ifid,int type,const unsigned char* data,
                         int len,simclick_simpacketinfo* pinfo);
  ClickEventHandler cevhandler_;

  // ExtRouter method
  virtual int route(Packet* p);

  string GetIPAddr(int ifid);
  string GetMACAddr(int ifid);
  string GetNodeName();
  int GetNodeAddr(); /// ToNSTrace
  int IFReady(int ifid);
  static void LinkLayerFailedCallback(Packet* p, void* arg);
  void LinkLayerFailed(Packet* p);
  void trace(char* fmt, ...); /// ToNSTrace
  int GetNextPktID(); /// ToNSTrace
  int GetIFID(const char *) const;
  static map<struct timeval,ClickEvent*> global_clickevents_; //added by wim 
vandenberghe to include an old performance patch of neufeld
 protected:
  int GetNSSubtype(int clicktype);
  int GetClickPacketType(int nssubtype);
  struct timeval GetSimTime();
  Packet* MakeRawPacket(int type,int ifid,const unsigned char* data,int len,
                        simclick_simpacketinfo* pinfo);
  typedef map<int,string> STRmap;
  map<int,string> ifipaddrs_;
  map<int,string> ifmacaddrs_;
  static map<MACAddr,int> global_mactonodemap_;
  static map<MACAddr,int> global_mactonsmacmap_;
  static map<u_int32_t,int> global_ipmap_;
  string nodename_;
  int nodeaddr_;
  bool click_initialized_;



  //mvhaen -- meant to allow a click router to add to an ns2 trace file.
  Trace *logtarget_;


};

#endif
_______________________________________________
click mailing list
[email protected]
https://amsterdam.lcs.mit.edu/mailman/listinfo/click

Reply via email to