 /*
 * $Id: ping.c,v 1.16 2000/07/23 00:55:29 remlali Exp $
 *                      P I N G . C
 *
 * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
 * measure round-trip-delays and packet loss across network paths.
 *
 * Author -
 *      Mike Muuss
 *      U. S. Army Ballistic Research Laboratory
 *      December, 1983
 * Modified at Uc Berkeley
 * Record Route and verbose headers - Phil Dykstra, BRL, March 1988.
 * Multicast options (ttl, if, loop) - Steve Deering, Stanford, August 1988.
 * ttl, duplicate detection - Cliff Frost, UCB, April 1989
 * Pad pattern - Cliff Frost (from Tom Ferrin, UCSF), April 1989
 * Wait for dribbles, option decoding, pkt compare - vjs@sgi.com, May 1989
 *
 * Status -
 *      Public Domain.  Distribution Unlimited.
 *
 * Bugs -
 *      More statistics could always be gathered.
 *      This program has to run SUID to ROOT to access the ICMP socket.
 *
 * More modifications to make this work with my snmp tool.  gregm@randomc.com
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "main.h"
#define _USE_BSD
#include <sys/types.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
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <errno.h>
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
#ifdef HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
/* #include <netinet/ip_var.h> */
#ifdef HAVE_SYS_FILIO_H  /* needed for solaris */
#  include <sys/filio.h>
#endif
#ifdef HAVE_FCNTL_H
#  include <fcntl.h>
#endif

#include "debug.h"

#ifndef HAVE_STRUCT_ICMP
/* This should only be the case on Linux Libc 5. */
typedef struct icmphdr		struct_icmp;

#define icmp_type type
#define icmp_code code
#define icmp_cksum checksum
#define icmp_id un.echo.id
#define icmp_seq un.echo.sequence
#define icmp_gwaddr un.gateway
#else
typedef struct icmp		struct_icmp;
#endif

extern gxsnmp *app_info;

#define MAXPACKET    (65536-60-8)    /* max packet size */
#define MAX_DUP_CHK  8 * 128
int  mx_dup_ck = MAX_DUP_CHK;
char rcvd_tbl [ MAX_DUP_CHK / 8 ];

#define A(bit)          rcvd_tbl[ (bit>>3) ]    /* identify byte in array */
#define B(bit)          ( 1 << (bit & 0x07) )   /* identify bit in byte */
#define SET(bit)        A(bit) |= B(bit)
#define CLR(bit)        A(bit) &= (~B(bit))
#define TST(bit)        (A(bit) & B(bit)) */

static u_char  *packet;
int     i;

struct sockaddr whereto;        /* Who to ping */
#define DATALEN 64-8
int datalen = DATALEN;          /* How much data */
int packlen=DATALEN+100;

/* static u_char outpack[MAXPACKET];  -- Not used? */

int ntransmitted = 0;           /* sequence # for outbound packets = #sent */
int ident;

struct sockaddr_in *to = (struct sockaddr_in *) &whereto;
/* static u_char *datap = &outpack[8+sizeof(struct timeval)]; -- Not used? */
gint   Pingsocket=0;

void 
ping_input_cb (gpointer data, gint source, GdkInputCondition condition)
{
  static int         seqnum = 0;
  u_long             nowtime;
  struct sockaddr_in *from;
  char               buf[256];
  struct in_addr     in;
  struct hostent     *hp;
  D_FUNC_START;
  nowtime = time (NULL);
  while ( (from = readping()) != NULL)
    {
      in.s_addr = (u_long)from->sin_addr.s_addr;
      hp = gethostbyaddr ((char *) &from->sin_addr.s_addr, 4, AF_INET);
      if (!hp)
	sprintf (buf, "Ping recieved from \"%s\".", inet_ntoa (in));
      else
	sprintf (buf, "Ping recieved from \"%s\", [%s].", hp->h_name,
		 inet_ntoa (in));
      update_statusbar (buf);
      g_free (from);
    }
  /*
   * clean up old children
   */
  if (++seqnum > 50)
    {
      while (wait3 ((union wait *)NULL, WNOHANG, (struct rusage *)NULL) >0 ) 
	;
      seqnum = 0;
    }
  D_FUNC_END;
}

void
initpingsocket() 
{
  struct protoent *proto;
  int         i_sock;
  
  D_FUNC_START;
  if(! Pingsocket) 
    {
      if((proto = getprotobyname("icmp")) == (struct protoent *)NULL) 
	{
	  fprintf(stderr, "icmp: unknown protocol\n");
	  exit(10);
	}
      
      if((Pingsocket = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) 
	{
	  perror("ping: socket");
	  exit(5);
	}
      /* make it nonblocking */
      /* i_sock = 1;
	 ioctl(Pingsocket, FIONBIO, &i); */
      fcntl(Pingsocket, F_SETFL, O_NONBLOCK);
    }
  D_FUNC_END;
}

void
initping ()
{
  D_FUNC_START;
  app_info->ping_tag = gdk_input_add (Pingsocket, GDK_INPUT_READ, 
				      (GdkInputFunction) ping_input_cb,
				      NULL);
  D_FUNC_END;
}

int 
sendping (u_long address)
{
  struct in_addr       if_addr;
  register struct_icmp *icp;
  int                  cc;
  char                 outpack[sizeof(struct_icmp)];
  D_FUNC_START;
  memset ((char *)&whereto, '\0', sizeof(struct sockaddr) );
  to->sin_family      = AF_INET;
  to->sin_addr.s_addr = address;
  if_addr.s_addr      = address;

  if (!ident)
    ident = getpid () & 0xFFFF;
  
  icp = (struct_icmp *)outpack;

  icp->icmp_type  = ICMP_ECHO;
  icp->icmp_code  = 0;
  icp->icmp_cksum = 0;
  icp->icmp_seq   = htons((unsigned short)(ntransmitted++));
  icp->icmp_id    = ident;
  CLR (ntohs(icp->icmp_seq) % mx_dup_ck);
  cc = datalen + 8;                          /* skips ICMP portion */
  icp->icmp_cksum = in_cksum ((u_short *)icp, cc);
  sendto (Pingsocket, (char *)outpack, cc, 0, &whereto, 
	  sizeof (struct sockaddr));
  D_FUNC_END;
  return 1;
}

struct sockaddr_in *
readping ()
{
  struct ip              *ip;
  register struct_icmp   *icp;
  int                    hlen;
  int                    i, cc;
  struct sockaddr_in     from;
  struct sockaddr_in     *ret;
  D_FUNC_START;
  if (!packet &&(packet = (u_char *)g_malloc(packlen)) == NULL )
    {
      g_print ("ping: g_malloc failed for %d bytes\n", packlen);
      perror ("");
      exit (1);
    }
  errno = 0;
  i     = sizeof(from);
  if ((cc = recvfrom (Pingsocket, (char *)packet, packlen, 0, &from, &i)) < 0)
    {
      D_FUNC_END;
      return NULL;
    }
  /*
    Lets check the ip header
  */
  ip   = (struct ip *)packet;
  hlen = ip->ip_hl << 2;
  icp  = (struct_icmp *)(packet + hlen);
  if (icp->icmp_type == ICMP_ECHOREPLY && icp->icmp_id == ident)
    {
      ret = (struct sockaddr_in *)g_malloc (sizeof (struct sockaddr_in));
      memcpy ((char *)ret, &from, sizeof (struct sockaddr_in));
      D_FUNC_END;
      return ret;
    }
  else
    {
      D_FUNC_END;
      return NULL;
    }
}

/*
 * IN_CKSUM
 *
 * Checksum routine for Internet Protocol family headers (C Version)
 *
 */
u_short
in_cksum(u_short *addr, int len)
{
  register int nleft  = len;
  register u_short *w = addr;
  register int sum    = 0;
  u_short answer      = 0;
  
  D_FUNC_START;
  /*
   *  Our algorithm is simple, using a 32 bit accumulator (sum),
   *  we add sequential 16 bit words to it, and at the end, fold
   *  back all the carry bits from the top 16 bits into the lower
   *  16 bits.
   */
  while( nleft > 1 )  
    {
      sum += *w++;
      nleft -= 2;
    }
  
  /* mop up an odd byte, if necessary */
  if( nleft == 1 ) 
    {
      *(u_char *)(&answer) = *(u_char *)w ;
      sum += answer;
    }
  
  /*
   * add back carry outs from top 16 bits to low 16 bits
   */
  sum    = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
  sum   += (sum >> 16);                      /* add carry */
  answer = ~sum;                             /* truncate to 16 bits */
  D_FUNC_END;
  return (answer);
}
/* EOF */





