/*
 * 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>

#if HAVE_MEMORY_H
# include <memory.h>
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#endif

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

#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif
#if HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif

#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>

#include <arpa/inet.h>

#define MP_EXTERN
#include "mplib.h"

void 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;   /* Perhaps this is necessary on the Cray but not any sane OS */
    nice(-40);
    if(errno) NOERROR(-1, "nice");
#elif HAVE_SETPRIORITY
    NOERROR(setpriority(PRIO_PROCESS, 0, -20), "setpriority");
#else
    NOERROR(nice(-20), "nice");
#endif /* CRAY / HAVE_SETPRIORITY */
}


/*
 * setsin - mash some form of a host name into a socket struct
 * BEWARE: does not set the port number
 */
int setsin(struct sockaddr_in *sin, char *name)
{
  struct hostent *hp;
  
  memset((char *)sin, 0, sizeof(struct sockaddr_in));
  if (isdigit(*name)) {
    sin->sin_addr.s_addr = inet_addr(name);
  }
  else if ((hp = gethostbyname(name))) {
    memcpy((char *)&sin->sin_addr, hp->h_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(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;
  
  memset(optlist, 0, 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
      memcpy(oix, ((char *)&route[i].sin_addr) + 4 /* crock */, 4);
#else
      memcpy(oix, &route[i].sin_addr, 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.... */
void crackip(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(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 */
