/*
 * icmp_pkt.c
 * by crisk
 *  
 * routines to send a custom icmp/ip packet over the net.
 * 
 * CHANGES: changed ttl value on both headers to be possible to such a packet. 
 * 
 */

#define IPHDRSIZE sizeof(struct iphdr)
#define ICMPHDRSIZE sizeof(struct icmphdr)

#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/ip.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
int cize;

/* ********** RIPPED CODE START ******************************** */

/*
 * in_cksum --
 *  Checksum routine for Internet Protocol family headers (C Version)
 */
unsigned short in_cksum(addr, len)
    u_short *addr;
    int len;
{
    register int nleft = len;
    register u_short *w = addr;
    register int sum = 0;
    u_short answer = 0;
 
    /*
     * 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 */
    return(answer);
}

/* ********** RIPPED CODE END ******************************** */


/*
 * icmp_unreach_send()
 * builds and sends an ICMP unreachable packet. Since ICMP unreachable packets
 * contain the IP header + 64 bits of original datagram, we create a bogus
 * IP header and the first 64 bits of a TCP header (ports and syn). 
 *
 */

 inline int icmp_unreach_send(int                socket, 
       			      struct sockaddr_in *address,
 			      unsigned char      icmp_code,
 			      unsigned long      spoof_addr,
			      unsigned long      s_addr,
			      unsigned long      t_addr,
			      unsigned           s_port,
			      unsigned           t_port,
			      unsigned long      seq)
     {
	unsigned char packet[4098];
	struct iphdr   *ip;
	struct icmphdr *icmp;
	struct iphdr   *origip;
	unsigned char  *data;
        int i;
	
	
	ip = (struct iphdr *)packet;
	icmp = (struct icmphdr *)(packet+IPHDRSIZE);
	origip = (struct iphdr *)(packet+IPHDRSIZE+ICMPHDRSIZE);
	data = (char *)(packet+IPHDRSIZE+IPHDRSIZE+ICMPHDRSIZE);
	
	memset(packet, 0, 4098);
	
	ip->saddr    = spoof_addr;
	ip->daddr    = t_addr;
	ip->version  = 4;
	ip->ihl      = 5; 
	ip->ttl      = 255-random()%15;
	ip->protocol = IPPROTO_ICMP;
	ip->tot_len  = htons(IPHDRSIZE + cize + ICMPHDRSIZE + IPHDRSIZE + 8);
	
	ip->check    = in_cksum(packet,IPHDRSIZE);
	
        origip->saddr    = t_addr;   /* this is the 'original' header. */
	origip->daddr    = s_addr;
	origip->version  = 4;
	origip->ihl      = 5;
	origip->ttl      = ip->ttl - random()%15;
	origip->protocol = IPPROTO_TCP; 
	origip->tot_len  = IPHDRSIZE + 30; 
	origip->id       = random()%69;
	
       	origip->check = in_cksum(origip,IPHDRSIZE);
	
	*((unsigned int *)data)          = htons(s_port);
	*((unsigned int *)(data+2))      = htons(t_port);
	*((unsigned long *)(data+4))     = htonl(seq);

	/* 'original IP header + 64 bits (of bogus TCP header)' made. */
	
	icmp->type = ICMP_ECHO; /* should be 3 */
	icmp->code = icmp_code;
	
	icmp->checksum = in_cksum(icmp,cize+ICMPHDRSIZE+IPHDRSIZE+8);

	/* the entire ICMP packet it now ready. */
		
#ifdef ICMP_PKT_DEBUG	
        printf("Packet ready. Dump: \n");
        for (i=0;i<IPHDRSIZE+ICMPHDRSIZE+IPHDRSIZE+8;i++)
	   printf("%02X%c",*(packet+i),((i+1)%16) ? ' ' : '\n');
        printf("\n");
#endif	
	
	return sendto(socket,packet,IPHDRSIZE+cize+ICMPHDRSIZE+IPHDRSIZE+8,0,
		      (struct sockaddr *)address,sizeof(struct sockaddr)); 
	
	/* ICMP packet is now over the net. */
	
     }
