/* $Id: sping.c,v 1.1 1999/08/23 16:18:40 naamato Exp $ */
/*
 * Copyright(c) 1998 by Hitachi.Ltd All rights reserved.
 *
 */

#include "defs.h"
#include <signal.h>
#include <netinet/ip_icmp.h>
#include <net/route.h>
#include <netinet/ip_mroute.h>
#include <netdb.h>

#define IF_NAME_SIZE 8
#define LOCAL_SCOPE_TTL 16

char usage[] = "Usage: sping [-i interface] [-s src_addr] [-t ttl] dst_addr\n";

int sock;
char send_buf[1024];
u_long icmp_src,icmp_dst;
u_char ttl;
int seq_no;
char *if_name;
void send_icmp();
void icmp_recv();
int ping_id;

main(argc, argv)
int argc;
char **argv;
{
    char opt;
    extern char *optarg;
    extern int errno, optind;
    u_long src,dst;

    u_long ifaddr;
    int len;
    char *buff;
    fd_set fdmask;
    struct timeval timeout;
    int bool;
    struct in_addr adr;
    struct hostent *hp;

    src = dst = 0;
    ttl = LOCAL_SCOPE_TTL;
    if_name = NULL;
    seq_no = 0;

    while((opt = getopt(argc,argv,"i:t:s:")) != -1) {
        switch(opt) {
	case 'i' : if_name = malloc(IF_NAME_SIZE);
	           strcpy(if_name,optarg);
	           break;
	case 't' : ttl = atoi(optarg);
	           break;
	case 's' : src = inet_stoa(optarg);
	           break;
	default:   fprintf(stderr,usage);
	           exit(1);
	}
    }
    argc -= optind;
    argv += optind;
    if(argc != 1) {
        fprintf(stderr,usage);
	exit(1);
    }
    if(**argv >= '1' && **argv <= '9') {   /* IP address */
	dst = inet_stoa(*argv);
    }
    else {                                 /* Host name */
	hp = gethostbyname(*argv);
	dst = ((struct in_addr *)(hp->h_addr))->s_addr;
	printf(" %s -> %s\n",*argv,inet_atos(dst));
	dst = ntohl(dst);
    }

    interface_init();
    
    if((sock = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) <0) {
	perror("ICMP socket");
	exit(1);
    }

    len = 1024;
    buff = malloc(len);

#ifdef IP_HDRINCL
    if(src) {
	bool = 1;
	if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL,(char *)&bool, sizeof(bool)) < 0)
	    perror("setsockopt IP_HDRINCL");
    }
#endif

    icmp_src = htonl(src);
    icmp_dst = htonl(dst);
    signal(SIGALRM,send_icmp);
    ping_id = getpid() & 0xFFFF;
    printf(" Ping to %s \n",inet_atos(icmp_dst));
    send_icmp();

    for(;;) {
	int cc,fromlen;
	struct sockaddr_in from;

	FD_ZERO(&fdmask);
	FD_SET(sock,&fdmask);

	timeout.tv_sec = 0;
	timeout.tv_usec = 0;	
	select(sock+1, &fdmask,NULL,NULL,&timeout);

	if(FD_ISSET(sock,&fdmask)) {
	    fromlen = sizeof(from);
	    if((cc=recvfrom(sock,buff,1024,0,(struct sockaddr *)&from,&fromlen)) < 0) {
		perror("recvfrom:");
		continue;
	    }
	    icmp_recv(buff,cc,&from);
	}
    }

}

void send_icmp()
{
    u_char loop;
    int rtn;
    struct sockaddr_in sdst;
    u_char ip_ttl;
    struct in_addr adr;
    struct ip *ip;
    struct icmp *icmp;
    char *packet;
    int pktlen;
    int iplen;
    u_long ifaddr;

    if(IN_MULTICAST(ntohl(icmp_dst))) {
	if(!ttl) ttl=1;
	if(setsockopt(sock,IPPROTO_IP,IP_MULTICAST_TTL,(char *)&ttl,sizeof(ttl))<0)
	    perror("setsockopt IP_MULTICAST_TTL");

	loop=0;
	if(setsockopt(sock,IPPROTO_IP,IP_MULTICAST_LOOP,(char *)&loop,sizeof(loop)) < 0)
	    perror(" setsockopt IP_MULTICAST_LOOP");

	if(if_name) {
	    ifaddr = get_ifaddr_byname(if_name);
	    adr.s_addr = htonl(ifaddr);
	    if(setsockopt(sock,IPPROTO_IP,IP_MULTICAST_IF,(char *)&adr, sizeof(adr)) < 0)
		perror("setsockopt IP_MULTICAST_IF");
	}
    }
    else {
	if(!ttl) ttl=255;
    }

    packet = send_buf;
    if(icmp_src) {
#ifdef IP_HDRINCL
	ip = (struct ip *)send_buf;

	ip->ip_v = IPVERSION;
	ip->ip_hl = sizeof(struct ip) >> 2;
	ip->ip_tos = 0;
	ip->ip_off = 0;
	ip->ip_p = IPPROTO_ICMP;
	ip->ip_ttl = ip_ttl = ttl;
	ip->ip_src.s_addr = icmp_src;
	ip->ip_dst.s_addr = icmp_dst;
	ip->ip_len = iplen = pktlen = sizeof(struct ip) + sizeof(struct icmp);

	icmp = (struct icmp *)(ip+1);
#endif
    }
    else {
	icmp = (struct icmp *)send_buf;
	pktlen = sizeof(struct icmp);
    }

    icmp->icmp_type = ICMP_ECHO;
    icmp->icmp_code = 0;
    icmp->icmp_cksum = 0;
    icmp->icmp_seq = seq_no++;
    icmp->icmp_id = ping_id;
    icmp->icmp_cksum = in_cksum((u_short *)icmp, sizeof(struct icmp));

    bzero(&sdst,sizeof(sdst));
    sdst.sin_family = AF_INET;
    sdst.sin_addr.s_addr = icmp_dst;
    if(sendto(sock, packet, pktlen, 0, (struct sockaddr *)&sdst,sizeof(sdst)) < 0) {
        perror("sendto");
    }

    alarm(1);
}

void icmp_recv(buf,cc,from)
char *buf;
int cc;
struct sockaddr_in *from;
{
    struct ip *ip;
    struct icmp *icmp;
    int hlen;

    ip = (struct ip *)buf;
    hlen = ip->ip_hl << 2;

    icmp = (struct icmp *)(buf + hlen);
    if(icmp->icmp_type == ICMP_ECHOREPLY) {
	if(icmp->icmp_id != ping_id)
	    return;
/*	printf(" send to %s ",inet_atos(icmp_dst));*/
	printf("  reply from %s : %d byte : seq %d\n",inet_atos(from->sin_addr.s_addr),cc,icmp->icmp_seq);
    }
}


