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

#include "defs.h"
#include <arpa/inet.h>
#include "igmp_def.h"

#define IF_NAME_SIZE 8

#define GETC(fp,argv)  (fp? getc(fp): *argv++)

char send_buf[1024];

char usage[] = "Usage: ippkt [-i interface] [-s src_addr] [-d dst_addr] [-f file] [-p protocol] [-C count] [ data... ]\n";
int time_expire;

main(argc, argv)
int argc;
char **argv;
{
    char opt,c,upper,lower;
    char *pkt,*base,*stream;
    extern char *optarg;
    extern int optind;
    u_long ifaddr;
    u_long src,dst;
    char *if_name;
    char flags;
    FILE *fp; 
    int count;
    struct ip *ip;
    int ip_sock;
    struct sockaddr_in sdst;
    int bool;
    struct in_addr adr;
    u_char ttl;
    int protocol;
    int i;
    int data_len,iplen,igmp_len,ip_hlen;
    struct igmp *igmp;

    src = dst = ifaddr = 0;
    protocol= 0;
    fp=NULL;
    count=1;
    ttl=1;

    while((opt = getopt(argc,argv,"i:s:d:f:C:p:t:")) != -1) {
        switch(opt) {
	case 'i' : if_name=malloc(IF_NAME_SIZE);
	           strcpy(if_name,optarg);
	           break;
	case 's' : src = inet_stoa(optarg);
	           break;
	case 'd' : dst = inet_stoa(optarg);
	           break;
	case 'f' : if(!(fp=fopen(optarg,"r"))) 
		      printf(" %s open error \n",optarg);
		   break;
	case 'C' : count = atoi(optarg);
	           break;
	case 'p' : protocol = atoi(optarg);
	           break;
	case 't' : ttl = atoi(optarg);
  	           break;
	default:   fprintf(stderr,usage);
	           break;
	}
    }
    argc -= optind;
    argv += optind;

    if(!fp && argc != 1) {
        fprintf(stderr,usage);
	exit(1);
    }

    if(!fp) {
	i=0;
	stream = *argv;
	printf(" %s \n",stream);
	while(*(stream+i)) {
	    i++;
	}
	*(stream+i) = EOF;
    }

    interface_init();
/*    igmp_init();*/

    data_len = 0;
    if(if_name) {
	ifaddr = get_ifaddr_byname(if_name);
	if(!src) src = ifaddr;
    }

    if(src && dst && protocol) {
	pkt = base = send_buf+IPHDR_LEN;
    }
    else {
	pkt = send_buf;
    }
    upper = lower = 0;
    while((c = GETC(fp,stream)) != EOF) {
        if( (c >= '0' && c <= '9') 
	  ||(c >= 'a' && c <= 'f')) {
	  if(!upper) upper = c;
	  else lower = c;
	}

	if(upper && lower) {
	    if(upper >= '0' && upper <= '9') {
		*pkt = (upper-'0') << 4;
	    }
	    else if(upper >= 'a' && upper <= 'f') {
		*pkt = 10+(upper-'a') << 4;
	    }

	    if(lower >= '0' && lower <= '9') {
		*pkt |= lower-'0';
	    }
	    else if(lower >= 'a' && lower <= 'f') {
		*pkt |= 10+lower-'a';
	    }

	    pkt++;
	    upper = lower = 0;
	}
    }

    if(src && dst && protocol) {
#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 = protocol;
	ip->ip_ttl = ttl;
	ip->ip_src.s_addr = htonl(src);
	ip->ip_dst.s_addr = htonl(dst);
	ip->ip_len = htons(pkt - base + IPHDR_LEN);
	data_len = pkt - base + IPHDR_LEN;
#endif
    }
    else {
        ip = (struct ip *)send_buf;
/*	src = ntohl(ip->ip_src.s_addr);
	dst = ntohl(ip->ip_dst.s_addr);*/
	data_len = pkt - send_buf;
    }

    if(protocol == 2) {
	ip_hlen = ip->ip_hl << 2;
	igmp = (struct igmp *)(send_buf+ip_hlen);
	igmp->igmp_cksum = 0;
	if(src && dst) 
	    igmp_len = data_len;
	else 
	    igmp_len = data_len - ip_hlen;

	igmp->igmp_cksum = in_cksum((u_short *)igmp, igmp_len);
    }
	
#if 1
/*    printf("%d \n",ntohs(ip->ip_len));*/
    printf("%s -> ",inet_atos(ip->ip_src.s_addr));
    printf(" %s : %d byte send (iplen %d)", inet_atos(ip->ip_dst.s_addr),data_len,ntohs(ip->ip_len));
    if(protocol == 2) {
	printf(" proto igmp : len %d,type %x,code %d\n",
	       igmp_len,igmp->igmp_type,igmp->igmp_code);
    }
    printf("\n");
#endif
    if(fp) fclose(fp);

    if((ip_sock=socket(AF_INET, SOCK_RAW, IPPROTO_IP)) < 0) {
       perror("IP socket");
       exit(1);
    }

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

    if(IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
       if (setsockopt(ip_sock,IPPROTO_IP,IP_MULTICAST_TTL, (char *)&ttl,sizeof(ttl)) < 0) 
          perror("setsockopt IP_MULTICAST_TTL");

       if(ifaddr) {
	   adr.s_addr = htonl(ifaddr);
	   if(setsockopt(ip_sock,IPPROTO_IP,IP_MULTICAST_IF,
			 (char *)&adr, sizeof(adr)) < 0)
	       perror("setsockopt IP_MULTICAST_IF");
       }

/*       loop=1;
       if(setsockopt(igmp_sock,IPPROTO_IP,IP_MULTICAST_LOOP,(char *)&loop,sizeof(loop)) < 0)
          perror("setsockopt IP_MULTICAST_LOOP");*/
    }

    ip->ip_len = ntohs(ip->ip_len);
    if(ip->ip_len >= data_len) iplen = ip->ip_len;
    else iplen = data_len;
    bzero(&dst,sizeof(sdst));
    sdst.sin_family = AF_INET;
    sdst.sin_addr.s_addr = ip->ip_dst.s_addr;
    if(sendto(ip_sock,(char *)ip,iplen,0,(struct sockaddr *)&sdst,sizeof(sdst)) < 0) {
      perror("send to");
    }
}
