/*
 * Multi Ping library functions
 *
 * (c) Pittsburgh Supercomputing Center, Matthew B Mathis, 1992.
 * All rights reserved.   See COPYRIGHT.h for additional information.
 *
 * Content:
 * setthrust() - More thrust if we are on a Cray
 * setsin(sin, name) - Mash host name into a socket struct
 * setroute(...) - general set route options
 * crackip(buff) - locate all headers within a packet
 * checksum(data, len) - a simple checksum
 *
 */
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <ctype.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/ip_var.h>

#define MP_EXTERN
#include "mplib.h"

setthrust()
{
#ifdef CRAY
    /* More thrust if we are on a Cray,  Claim "Real Time",
       so we get our cycles even in the presence of gready users */
    int cpufd;
    static struct cpudev cdev = { 0 };

    NOERROR(cpufd=open("/dev/cpu/any", O_RDONLY, 0), "Open /dev/cpu");
    cdev.cat = C_PROC;
    cdev.id = 0;
    NOERROR(ioctl(cpufd, CPU_SETRT, &cdev), "ioctl set RT");
    NOERROR(close(cpufd), "close cpufd");
    errno=0;
    nice(-40);
    if(errno) NOERROR(-1, "nice");
#else /* CRAY */
    errno=0;
    nice(-20);
    if(errno) NOERROR(-1, "nice");
#endif /* CRAY */
}


/*
 * setsin - mash some form of a host name into a socket struct
 * BEWARE: does not set the port number
 */
int setsin(sin, name)
     struct sockaddr_in *sin;
     char *name;
{
  struct hostent *hp;
  
  bzero((char *)sin, sizeof(struct sockaddr_in));
  if (isdigit(*name)) {
    sin->sin_addr.s_addr = inet_addr(name);
  }
  else if (hp = gethostbyname(name)) {
    bcopy(hp->h_addr, (char *)&sin->sin_addr, hp->h_length);
  }
  else {
    printf("I Don't understand host/gateway name %s\n",name);
    exit(2);
  }
  sin->sin_family = AF_INET;
  sin->sin_port = 0;		/* must be set again elsewhere */
  return(1);
}

/* general set route options */
int setroute(sock, mode, rlen, route, rr)
     int sock;			/* the socket to set */
     int mode;			/* 0 (none), LSRR or SSRR */
     int rlen;			/* number of the above */
     struct sockaddr_in *route;	/* the requested route */
     int rr;				/* number of record route, -1 => MAX */
{
  u_char optlist[MAXOPTS], *oix;
  
  bzero(optlist, MAXOPTS);		/* all end of options */
  oix = optlist;
  if (mode && (rlen>1)) {
    int i;
    
    *oix++ = mode;			/* BUG does not enforce LSRR/SSRR */
    *oix++ = 3+rlen*4;
    *oix++ = 4;
    for (i=0; i<rlen; i++) {
#ifdef CRAY
      bcopy(((char *)&route[i].sin_addr) + 4 /* crock */, oix, 4);
#else
      bcopy(&route[i].sin_addr, oix, 4);
#endif
      oix +=4;
    }
  }
  if (rr<0) 
    rr = (oix-optlist-3)/4;
  if (rr>0) {
    *oix++ = IPOPT_RR;		/* record route */
    *oix++ = 3 + rr*4;
    *oix++ = 4;
    oix += rr*4;
  }
  while ((oix-optlist)&3) oix++;	/* force mult of 32 bits */
  if (optlist-oix) {
    NOERROR(setsockopt(sock, 0, IP_OPTIONS, optlist, oix-optlist),
	    "set sockoptions");
  }
  return((oix-optlist)?(oix-optlist-4):0);	/* deduct initial host */
}

/* #define WHYRET(str) printf(str) */
#define WHYRET(str) /* nothing */
/* crackip - a general routine for locating all of the headers within ip */
/* BUG this should be passed a structure rather than using globals but.... */
crackip(buff)
     u_char * buff;
{
  cip = 0; cipopt = 0; cipolen = 0; cicmp = 0;
  cip2 = 0; cipopt2 = 0; cipolen2 =0; cipclient = 0;
  
  cip = (struct ip *) buff;
  if ((cip->ip_v != 4) || (cip->ip_hl <5)) {
    cip = 0;
WHYRET("cip->ip_v\n");
    return;
  }
  buff += SIZEOFstructip;
  
  if (cip->ip_hl != 5) {
    cipopt = buff;
    buff += cipolen = (cip->ip_hl - 5) * 4;
#ifdef CRAY
    if (cipolen%8) {
      printf("Can not parse packets w/ options which are not full words\n");
      return;
    }
#endif
  }

  if (cip->ip_p != IPPROTO_ICMP) {
    cipclient = buff;	/* immediately follows the ip header */
WHYRET("cip->ip_p != IPPROTO_ICMP\n");
    return;
  }
 
  cicmp = STRUCTALIGN(struct icmp32o *, buff, 4);
  buff += ICMP_MINLEN;

  cip2 = STRUCTALIGN(struct ip32o *, buff, 4);
  if ((cip2->ip_v != 4) || (cip2->ip_hl <5)) {
    cip2 = 0;
WHYRET("cip2->ip_v\n");
    return;
  }
  buff += SIZEOFstructip;
  
  if (cip2->ip_hl != 5) {
    cipopt2 = buff;
    buff += cipolen2 = (cip2->ip_hl - 5) * 4;
#ifdef CRAY
    if (cipolen2%8) {
      printf("Can not parse packets w/ options which are not full words\n");
      return;
    }
#endif
  }
  
  cipclient = buff;	/* immediately follows the ip header */
  return;
}

#ifndef CRAY
/* a simple checksum */
u_short checksum(data, len)
     register u_short *data;
     register int len;
{
  register u_long sum;
  sum = 0;
  
  while(len>1) {
    sum += *data++;
    len -= 2;
  }
  
  if (len) {
    sum += *data & ntohs(0xFFFF0000);
  }
  sum = (sum>>16) + (sum&0xFFFF);
  sum += (sum>>16);
  return((u_short) (~sum));
}

#else /* CRAY */

/* Nothing is simple on the cray */
u_short checksum(data, len)
     register u_long *data;
     register int len;
{
  register u_long sum;
  sum = 0;

  /* 8 bytes at a time */
  while(len>7) {
    sum += ((*data>>32) & 0xFFFFFFFF) + (*data & 0xFFFFFFFF);
    data++;
    len -= 8;
  }

  switch (len) {
  case 0:
    break;
  case 1:
    sum += ((*data>>32) & 0xFF000000); break;
  case 2:
    sum += ((*data>>32) & 0xFFFF0000); break;
  case 3:
    sum += ((*data>>32) & 0xFFFFFF00); break;
  case 4:
    sum += ((*data>>32) & 0xFFFFFFFF); break;
  case 5:
    sum += ((*data>>32) & 0xFFFFFFFF) + (*data & 0xFF000000); break;
  case 6:
    sum += ((*data>>32) & 0xFFFFFFFF) + (*data & 0xFFFF0000); break;
  case 7:
    sum += ((*data>>32) & 0xFFFFFFFF) + (*data & 0xFFFFFF00); break;
  }
  
/* Fold 32+some bits into 16 bits ones complement */
  sum = (sum>>16) + (sum&0xFFFF);
  sum = (sum>>16) + (sum&0xFFFF);

  return((u_short) (sum^0xFFFF));
}
#endif /* CRAY */
