/* 
 * sim_net.c
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1996,1993,1991,1990  Arizona Board of Regents
 */

#include <sys/types.h>
#include "x_stdio.h"
#include "xkernel.h"
#include "eth.h"
#include "eth_i.h"
#include "ip.h"
#include "ip_i.h"
#include "sim_i.h"
#include <stdio.h>
#define SIMPACKET_ALLOC_SIZE	20
#define CALLBACK_ALLOC_SIZE	20

/* static ETHhost		ethBcastHost = ETH_BCAST_HOST; */
/* static IPhost		ipLocalBcastHost = {255,255,255,255}; */
static IPhost		ipLocalHost = {0,0,0,0};
static SimPacket 	*freePacketList=NULL;
static CallBack		*freeCallBackList=NULL;

#ifdef XMEMTRACK

static int TrackId = 0;
extern char *xMallocTrack(unsigned, int);
extern void *xMallocZeroTrack(unsigned, int);
extern int  TrackGetId(char *);

#define xMalloc(s)     xMallocTrack(s, TrackId)
#define xMallocZero(s) xMallocZeroTrack(s, TrackId)

#endif
static int MemId = 0;
static int PktId = 0;
static int NewCount=0;
static int fooCount=0;
static int FreeCount=0;
static SimPacket *mainList=NULL;
static void 		send_on_net_now (SimPacket *);
extern long		lrand48(void);

static SimPacket
*newSimPacket ()
{
  int		k;
  SimPacket 	*sp;
  SimPacket *foo;
  int flag=0;
fooCount++;
  if (freePacketList == NULL) {
/*printf("Allocating new memory\n");*/
NewCount+=SIMPACKET_ALLOC_SIZE;

#ifdef XMEMTRACK
    if (TrackId == 0)
      TrackId = TrackGetId("sim_net");
#endif
    sp = freePacketList = (SimPacket *)xMallocZero(sizeof(SimPacket)*
					      SIMPACKET_ALLOC_SIZE);
    mainList = sp;
    for (k=0; k<SIMPACKET_ALLOC_SIZE-1; k++) {
      sp->next = sp + 1;
      sp++;
    }
    sp->next = NULL;
  }
/*  printf("Allocating Packet:");*/
  sp = freePacketList;
  freePacketList = sp->next;
  sp->flags = 0;
  return sp;
}
void
freeSimPacket (SimPacket *sp)
{
  SimPacket *foo;
  FreeCount++;
  sp->next = freePacketList;
  freePacketList = sp;
}

void
dumpList(SimPacket *foo) {
  printf("**************DUMP LIST **************\n");
  while (foo!=NULL) {
    printf("Id :%d\n", foo->id);
    foo=foo->next;
  }
}
static CallBack
*newCallBack ()
{
  int		k;
  CallBack 	*cbp;

  if (freeCallBackList == NULL) {
#ifdef XMEMTRACK
    if (TrackId == 0)
      TrackId = TrackGetId("sim_net");
#endif
    cbp = freeCallBackList = (CallBack *)xMallocZero(sizeof(CallBack)*
					       CALLBACK_ALLOC_SIZE);
    for (k=0; k<CALLBACK_ALLOC_SIZE-1; k++) {
      cbp->next = cbp + 1;
      cbp++;
    }
    cbp->next = NULL;
  }
  MemId++;
  cbp = freeCallBackList;
  freeCallBackList = cbp->next;
  
  return cbp;
}
    
void
freeCallBack (CallBack *cbp)
{
  cbp->next = freeCallBackList;
  freeCallBackList = cbp;
  MemId--;
}

SimPacket
*sim_create_eth_packet (SIM_PSTATE *ps, Net *net, Msg *msg, ETHhdr *hdr)
{
  SimPacket 	*pkt;
  static long	id=0;

  pkt = newSimPacket();
  xsimDbg(SL1_FLAG,
	  printf("               create_eth_packet for msg: %x  (pkt:%x)\n", 
		 msg, pkt));
  pkt->type = TYPE_ETH;
  pkt->id = id++;
/*  printf("Pkt->id is %d memory is %d \n",pkt->id,NewCount);*/
/*  printf("Allocated is %d Freed: %d\n",fooCount,FreeCount);*/
  pkt->net = net;
  msgConstructCopy(&pkt->msg, msg);
  pkt->len = msgLength(msg);
  pkt->flags = 0;
  pkt->src.type = pkt->hop.type = pkt->dst.type = TYPE_ETH;
  pkt->origSrc.addr.eth = pkt->src.addr.eth = hdr->src;
  pkt->hop.addr.eth = pkt->dst.addr.eth = hdr->dst;
  pkt->ps = ps;
  xsimDbg(SL1_FLAG,"Creating new sim packet: ");
  xsimDbg(SL1_FLAG, sim_print_packet(pkt));
  return pkt;
}

int
simGetHost (SIM_PSTATE *ps, Simhost *hp)
{
  int	host=0;
  Key 	*kp;

  if (hp->type == TYPE_ETH) {
    if ((kp = sim_addr2key(ps, (char *)&(hp->addr.eth), TYPE_ETH)) 
	!=  NULL) 
      host = kp->host;
  }
  else if (hp->type == TYPE_IP) {
    if ((kp = sim_addr2key(ps, (char *)&(hp->addr.ip), TYPE_IP)) 
	!= NULL)
      host = kp->host;
  }
  else 
    printf("ERROR: unknown host in simGetHost\n");
  return host;
}

static void
net_not_busy(Event ev, void *arg)
{
  int 		k, host;
  Net 		*net=(Net *)arg;
  SimPacket 	*pkt, *tpkt;
  CallBack	*cbp;
  XTime 	xtn;



  /* --- There were no collisions (or errors) so tell sender that the 
     packet has been put into the net successfully and deliver packet
     to other end */
  if (net->sent != NULL) {
    pkt = (SimPacket *)net->sent->arg2;
xGetTime(&xtn);
  xsimDbg(SL1_FLAG, printf("[net not busy] time:%lu, net:%d %s, pkt:%ld\n", 
			(xtn.sec*1000000+xtn.usec),net->id,
			   sim_addr2str(&net->net, TYPE_IP),pkt->id));
    xsimDbg(SL1_FLAG, printf("               Sent Succesfully\n"));
    net->sent->rv = RV_SUCCEED;
    xsimSetCurHost(net->sent->host);
    evDetach(evSchedule(net->sent->fun, (void *)net->sent, 0));  
    pkt = (SimPacket *)net->sent->arg2;
    if (pkt->flags & PKT_FLAG_BCAST) {
      pkt->flags &= ~PKT_FLAG_BCAST;
      for (k=0; k<net->numHosts; k++) {
	if (k == net->numHosts-1)
	  tpkt = pkt;
	else {
	  tpkt = newSimPacket();
	  *tpkt = *pkt;
	}
	tpkt->dst.addr.eth = net->allHosts[k].addr.eth;
	host = simGetHost(tpkt->ps, &tpkt->hop);
	xsimSetCurHost(host);
	evDetach(evSchedule(net->handler, (void *)tpkt, net->curDelay));
      }
    }
    else {
      host = simGetHost(pkt->ps, &pkt->hop);
      xsimSetCurHost(host);
      evDetach(evSchedule(net->handler, (void *)pkt, net->curDelay));
    }
    net->sent = NULL;
  }

  net->busy = 0;
  net->busyUntil = 0;
  if (net->notBusyBeg != NULL) {
    xsimDbg(SL2_FLAG, printf("               Doing net not busy callbacks! "));
  }
  while ((cbp = net->notBusyBeg) != NULL) {
    xsimDbg(SL2_FLAG, printf("%d ", cbp->host));
    net->notBusyBeg = cbp->next;
    xsimSetCurHost(cbp->host);
    evDetach(evSchedule(cbp->fun, (void *)cbp, 0));
  }
  net->notBusyEnd = NULL;
  xsimDbg(SL2_FLAG, printf("\n"));
  xsimSetCurHost(0);
  evDetach(ev);
}

static void
send_on_net_now (SimPacket *pkt)
{
  int  len=pkt->len, curHost=0;
  long  now;	/* VENKAT */
  unsigned long t;
  Net   *net=pkt->net;
  XTime xtnow;

  curHost = xsimGetCurHost();
  xGetTime(&xtnow);
  now = xtnow.sec*1000000 + xtnow.usec;
  xsimDbg(SL1_FLAG,
	  printf("               send_on_net_now net:%d pkt:%lu now:%lu busyUntil:%d\n",
		 net->id, pkt->id, now, net->busyUntil));
  xsimDbg(SL1_FLAG,
	  printf("                               timeUsed:%d\n", 
		 net->timeUsed));

  if (net->busy || net->busyUntil > now) {
    printf("ERROR: net busy in send_on_net_now!\n");
    return;
  }
  net->busy = 1;
  net->sendTime = now;
  len += net->packetHeader;
  if (len < net->minPacketSize)
    len = net->minPacketSize;
  len += net->packetOverhead;
  pkt->bwLen = len;
#if ~0UL == 0xffffffff
  if (net->rate % 1000 == 0)
    t = (((unsigned long)len)*1000)/(net->rate/1000);
  else {
    t = ((len % 2000) * 1000000)/net->rate;
    if (len >= 2000)
      t += (len / 2000) * (((unsigned long)2000000000)/net->rate);
  }
#else 
  t = (((unsigned long)len)*1000000)/net->rate;
   #endif 
  if (net->delayVar > 0) 
    t += lrand48() % net->delayVar;
  net->timeUsed += t;
  xsimSetCurHost(0);
  net->notBusyEv = evSchedule(net_not_busy, (void *)net, t);
  net->busyUntil = now + t;
  xsimDbg(SL1_FLAG,
	  printf("                           t: %d,  ", t));
  if (net->delayFun != NULL)
    t = net->delayFun(pkt->len);
  else
    t = net->delay;
  xsimDbg(SL1_FLAG,
	  printf("delay: %d\n", t));
  net->curDelay = t;
  xsimSetCurHost(curHost);
  xsimDbg(SL1_FLAG,
    printf("Done with send_on_net_now %ld\n",pkt->id));
}

/* --- Need to call it as the right host */
int
defaultNetControl (Net *np, int op, void (*cbFun)(), void *arg1, void *arg2, 
		   void *arg3)
{
  int	    curHost;
  long 	    now; /* VENKAT: Changed this from int */
  XTime	    xtnow;
  CallBack  *cbp;
  SimPacket *pkt;

  switch (op) {

  case OP_ISBUSY:
    if (np->busy) {
      /* --- If we want collisions */
      if (np->time2bBusy) {
	xGetTime(&xtnow);
	now = xtnow.sec*1000000 + xtnow.usec;
	if ((now - np->sendTime) <= np->time2bBusy) {
	  xsimDbg(SPC_FLAG, printf("[defaultNetControl] net:%s, op:%s\n",
				   ipHostStr(&np->net), 
				   "ISBUSY - it is, but will say it is NOT"));
	  return 0;
	}
      }
      xsimDbg(SL2_FLAG, printf("[defaultNetControl] net:%s, op:%s\n",
			       ipHostStr(&np->net),
			       "ISBUSY - YES"));
      return 1;
    }
    else {
      xsimDbg(SL2_FLAG, printf("[defaultNetControl] net:%s op:%s\n", 
			       ipHostStr(&np->net),
			       "ISBUSY - NO"));
      return 0;
    }
    
  case OP_NOTBUSY_CB:
    xsimDbg(SL2_FLAG, printf("[defaultNetControl] net:%s op:%s\n", 
			     ipHostStr(&np->net),
			     "NOTBUSY_CB"));
    cbp = newCallBack();
    cbp->fun = cbFun;
    cbp->state = arg1;
    cbp->op = OP_NOTBUSY_CB;
    cbp->host = xsimGetCurHost();
    cbp->arg1 = arg2;
    if (!np->busy) {
      evDetach(evSchedule(cbp->fun, (void *)cbp, 0));
    } else {
      if (np->notBusyBeg == NULL) 
	np->notBusyBeg = np->notBusyEnd = cbp;
      else {
	np->notBusyEnd->next = cbp;
	np->notBusyEnd = cbp;
      }
      cbp->next = NULL;
/*       cbp->next = np->notBusy; */
/*       np->notBusy = cbp; */
    }
    break;
    
  case OP_SEND:
    xsimDbg(PKT_FLAG,
	    printf("[defaultNetControl] net:%s op:%s\n", 
			     ipHostStr(&np->net),
			     "SEND"));
    xsimDbg(SL2_FLAG, printf("[defaultNetControl] net:%s op:%s\n", 
			     ipHostStr(&np->net),
			     "SEND"));
    cbp = newCallBack();
    if (cbp!=NULL)
      xsimDbg(SL1_FLAG,"New Callback succeeded\n");
    cbp->fun = cbFun;
    cbp->state = arg1;
    cbp->op = OP_SENT_CB;
    cbp->host = xsimGetCurHost();
    cbp->arg1 = arg2;
    cbp->arg2 = arg3;
    pkt = (SimPacket *)arg3;
    if (pkt->len > np->maxPacket) {
      xsimDbg(SPC_FLAG, printf("                    packet too big!\n"));
      cbp->rv = RV_FAILED;
      evDetach(evSchedule(cbp->fun, (void *)cbp, 10));
    }
    else if (np->busy) {
      xsimDbg(SPC_FLAG, printf("                    is busy!\n"));
      cbp->rv = RV_FAILED;
      evDetach(evSchedule(cbp->fun, (void *)cbp, 10));
      /* --- If we want collisions */
      if (np->collisionDelay) {
	xsimDbg(SL2_FLAG, printf("                    doing collisions!\n"));
	if (np->sent != NULL) {
	  xsimDbg(SPC_FLAG, printf("                    *COLLISION*\n"));
	  np->sent->rv = RV_FAILED;
	  curHost = xsimGetCurHost();
	  xsimSetCurHost(np->sent->host);
	  evDetach(evSchedule(np->sent->fun, (void *)np->sent, 0));
/* 	  xsimSetCurHost(curHost); */
	  np->sent = NULL;
	  np->collisionCnt++;
	}
	xsimSetCurHost(0);
	if (np->notBusyEv)
	  if (evCancel(np->notBusyEv) != EVENT_CANCELLED)
	    printf("\nERROR: Could not cancel event net_not_busy a/coll\n");
	np->notBusyEv = evSchedule(net_not_busy, (void *)np, 
				   np->collisionDelay);
	xsimSetCurHost(curHost);
	return 0;
      }
    } else {
      xsimDbg(PKT_FLAG,
	      printf("Sending on net now pkt: %ld \n",((SimPacket*)arg3)->id));
      np->sent = cbp;
      send_on_net_now((SimPacket *)arg3);
    }
    break;

  default:
    printf("ERROR in defaultNetControl, unknown opcode:%d\n", op);
    return -1;
  }
  return 0;
}

/*  
 * checksum over the the header is written into the checksum field of
 * *hdr.
 */ 
long
ipStdHdrLoad(hdr, src, len, arg)
    VOID *hdr;
    char *src;
    long int len;
    VOID *arg;
{    
    xAssert(len == sizeof(IPheader));
    bcopy(src, (char *)hdr, sizeof(IPheader)); 
    ((IPheader *)hdr)->checksum =
      ~ ocsum((u_short *)hdr, sizeof(IPheader) / 2) & 0xFFFF;
    ((IPheader *)hdr)->dlen = ntohs(((IPheader *)hdr)->dlen);
    ((IPheader *)hdr)->ident = ntohs(((IPheader *)hdr)->ident);
    ((IPheader *)hdr)->frag = ntohs(((IPheader *)hdr)->frag);
    return sizeof(IPheader);
}

long
tcpStdHdrLoad(hdr, src, len, arg)
VOID *hdr;
char *src;
long int len;
VOID *arg;
{
    xAssert(len == sizeof(tcpHDR));
    bcopy(src, hdr, len);

    ((tcpHDR *)hdr)->th_sport = ntohs(((tcpHDR *)hdr)->th_sport);
    ((tcpHDR *)hdr)->th_dport = ntohs(((tcpHDR *)hdr)->th_dport);
    ((tcpHDR *)hdr)->th_seq   = ntohl(((tcpHDR *)hdr)->th_seq);
    ((tcpHDR *)hdr)->th_ack   = ntohl(((tcpHDR *)hdr)->th_ack);
    ((tcpHDR *)hdr)->th_win   = ntohs(((tcpHDR *)hdr)->th_win);
    ((tcpHDR *)hdr)->th_urp   = ntohs(((tcpHDR *)hdr)->th_urp);

    return sizeof(tcpHDR);
}

void
sim_ethHandler (Event e, void *d)
{
  SimPacket     *pkt=(SimPacket *)d;
  Key		*kp;
  Protl		ulp;
  ROUTER_STATE	*rs;

  xsimDbg(SL2_FLAG,
	  printf("%s ethHandler for packet %ld\n", sim_getTime(), 
		 pkt->id));
  xsimDbg(SL1_FLAG, sim_print_packet(pkt));
  if ((kp = sim_addr2key(pkt->ps, (char *)&(pkt->hop.addr.eth), TYPE_ETH)) == 
      NULL) {
    printf("             ERROR in ethHandler, unknown host: %s\n", 
	   sim_simaddr2str(&(pkt->hop)));
    msgDestroy(&pkt->msg);
    freeSimPacket(pkt);
    return;
  }

  if (kp->objType == OBJTYPE_XOBJ) {
    ulp = (Protl) kp->obj;
    xsimDbg(SL2_FLAG,
	    printf("             demuxing to %s\n", ulp->fullName));
    ulp->demux(ulp, NULL, &pkt->msg);
    /* --- eth protocol destroys msg! */
    freeSimPacket(pkt);
  }
  else {
    rs = (ROUTER_STATE *)kp->obj;
    xsimDbg(SL2_FLAG, 
	    printf(">Calling inFun in router `%s' for packet %ld\n", 
		   rs->name, pkt->id));
    rs->inFun(rs, pkt, kp->inPort);
  }

  xsimDbg(SL1_FLAG,
	  printf("             end of ethHandler for packet %ld\n", 
		 pkt->id));
  return;
}

void
sim_ipHandler (Event e, void *d)
{
  SimPacket     *pkt=(SimPacket *)d;
  Key		*kp;
  Protl		ulp;
  ROUTER_STATE	*rs;

  xsimDbg(SL2_FLAG, 
	  printf("%s ipHandler for packet %ld\n", sim_getTime(), 
		 pkt->id));
  xsimDbg(SL2_FLAG, sim_print_packet(pkt));
  if ((kp = sim_addr2key(pkt->ps, (char *)&(pkt->hop.addr.ip), TYPE_IP)) == 
      NULL) {
    if ((kp = sim_addr2key(pkt->ps, (char *)&(pkt->hop.addr.eth), TYPE_ETH)) ==
        NULL) {
      printf("             ERROR in ipHandler, unknown host: %s\n", 
	     sim_simaddr2str(&(pkt->hop)));
      msgDestroy(&pkt->msg);
      freeSimPacket(pkt);
      return;
    }
  }

  if (kp->objType == OBJTYPE_XOBJ) {
    ulp = (Protl) kp->obj;
    xsimDbg(SL2_FLAG,
	    printf("             demuxing to %s\n", ulp->fullName));
    ulp->demux(ulp, NULL, &pkt->msg);
    /* --- eth protocol destroys msg! */
/*    printf("Demuxed %ld to %s\n",pkt->id,ulp->fullName);*/
    freeSimPacket(pkt);
/*    printf("Freed %ld \n",pkt->id);*/
  }
  else {
    rs = (ROUTER_STATE *)kp->obj;
    xsimDbg(SL2_FLAG, 
	    printf(">Calling inFun for router `%s' for packet %ld\n", 
		   rs->name, pkt->id));
    rs->inFun(rs, pkt, kp->inPort);
  }
  xsimDbg(SL1_FLAG, 
	  printf("             end of ipHandler for packet %ld\n", 
		 pkt->id));
  return;
}

static IPhost defRoute={0,0,0,0};
static IPhost leRoute={1,0,0,0};
static IPhost geRoute={2,0,0,0};

int
sim_getNextHop (ROUTER_STATE *rs, IPhost *ipHost, int *outPort, 
		Simhost *nextHop)
{
  int		i, k, out;
  RouteEntry 	*rep;
  Key		*kp;

  for (k=0,rep=rs->Routes; k<rs->numRoutes; k++,rep++) {
    if (IP_AND_EQUAL(*ipHost, rep->mask, rep->net) ||
       (rep->net.a == 1 && ipHost->c <= rep->net.c) ||
       (rep->net.a == 2 && ipHost->c >= rep->net.c) ||
       (rep->net.a == 3 && ipHost->c >= rep->net.b &&
       ipHost->c <= rep->net.c) || IP_EQUAL(defRoute, rep->net)) {
      out = rep->outPort;
      if (IP_EQUAL(rep->hop.addr.ip, ipLocalHost)) {
	kp = sim_addr2key(rs->ps, (char *)ipHost, TYPE_IP);
	nextHop->type = TYPE_ETH;
	nextHop->addr.eth = kp->ethAddr;
        if (out == -1) {
          for (i=0; i<rs->connectCnt; i++)
            if (IP_AND_EQUAL(*ipHost, rs->outPortNets[i]->mask,
                             rs->outPortNets[i]->net)) {
              out = i;
              break;
            }
        }
      }
      else {
	*nextHop = rep->hop;
      }
      if (out < 0) {
        char s[100];

        sprintf(s, "ERROR: outPort = %d in %s, for [%s], next hop: [%s]\n",
                out, rs->name, sim_addr2str(ipHost, TYPE_IP),
                sim_simaddr2str(nextHop));
        Kabort(s);
      }
      *outPort = out;
      xsimDbg(SL2_FLAG, 
	      printf(">    In %s, for [%s], next hop: [%s], outPort: %d\n", 
		     rs->name, sim_addr2str(ipHost, TYPE_IP), 
		     sim_simaddr2str(nextHop), out));
      return 0;
    }
  }
/*
  rep--;
  if (IP_EQUAL(defRoute, rep->net)) {
    out = rep->outPort;
    if (IP_EQUAL(rep->hop.addr.ip, ipLocalHost)) {
      kp = sim_addr2key(rs->ps, (char *)ipHost, TYPE_IP);
      nextHop->type = TYPE_ETH;
      nextHop->addr.eth = kp->ethAddr;
      if (out == -1) {
        for (i=0; i<rs->connectCnt; i++)
          if (IP_AND_EQUAL(*ipHost, rs->outPortNets[i]->mask,
                           rs->outPortNets[i]->net)) {
            out = i;
            break;
          }
      }
    }
    else {
      *nextHop = rep->hop;
    }
    *outPort = out;
    xsimDbg(SL2_FLAG, 
	    printf(">    In %s, for [%s], next hop: [%s] (DEFAULT), outPort: %d\n", 
		   rs->name, sim_addr2str(ipHost, TYPE_IP), 
		   sim_simaddr2str(nextHop), out));
    return 0;
  }
*/
  return 1;
}
