ITRACE

Description
itrace est un traceroute classique, utilisant la méthode du TTL trop court.
Il commence par envoyer un paquet ICMP echo avec un TTL de 1. Le premier routeur qui le reçoit considère que le paquet est tombé en timeout : il le détruit et envoie un message d'erreur ICMP (ICMP_TIME_EXCEEDED) à l'envoyeur (c'est à dire la machine sur laquelle s'execute itrace).
Il ne reste plus qu'à récupérer l'adresse IP source de ce paquet pour connaitre l'adresse du premier routeur qui se trouve sur le chemin.
On recommence l'opération (en incrémentant à chaque fois le TTL, pour que le paquet atteigne le routeur suivant), jusqu'à atteindre notre cible.

Compilation
Source Linux: gcc itrace.c -o itrace

Utilisation
./itrace hôte_cible [interface]
- hote_cible : machine de destination
- interface : interface réseau à utiliser pour trouver l'adresse IP locale

/*
Un traceroute classique
gcc itrace.c -o itrace */
#include <stdio.h> #include <string.h> #include <netdb.h> /* gehostbyname(), ... */ #include <net/if.h> /* struct ifreq */ #include <sys/ioctl.h> /* ioctl(), constantes, ... */ #include <sys/socket.h> /* socket(), sendto(), ... */ #include <arpa/inet.h> /* inet_ntoa(), ... */ #include <netinet/ip.h> /* struct iphdr */ #include <netinet/ip_icmp.h> /* struct icmphdr */ #include <sys/time.h> /* gettimeofday() */ #include <unistd.h> int time_diff(struct timeval *, struct timeval *); /* Calcule la différence en ms entre deux structures timeval */ int time_diff(struct timeval *tvdeb, struct timeval *tvfin) { int val; val=tvfin->tv_sec-tvdeb->tv_sec; val*=1000000; val+=tvfin->tv_usec; val-=tvdeb->tv_usec; return (val/1000); } /* */ int main(int argc, char **argv) { /* Descripteur de socket */ int fd; int r; /* Taille du paquet */ int pktsize=sizeof(struct iphdr)+sizeof(struct icmphdr)+32; /* Buffers accueillant les paquets à envoyer et à recevoir */ char buffer[256], recv_buffer[256]; /* Structure utilisée par ioctl() */ struct ifreq ifr; int sin_len=sizeof(struct sockaddr); /* Utilisée par sendto() et recvfrom() */ struct sockaddr_in saddr_in, saddr_in_from; /* Pour la résolution des adresses IP */ struct hostent *host; /* Structure représentant le format de l'entete IPv4 */ struct iphdr *ip, *ip_resp; /* Structure représentant le format de l'entete ICMP */ struct icmphdr *icmp; /* Liste de fd utilisée en interne par select() */ fd_set fdsr; /* Timeout pour select() */ struct timeval tv_timeout; /* Heures d'envoi et de reception des paquets */ struct timeval tv_benchs, tv_benchr; int err=0; if (argc<2) { printf("%s target [interface]\n", argv [0]); return -1; } /* Creation raw socket pour icmp */ if ((fd=socket(AF_INET, SOCK_RAW, IPPROTO_ICMP))==-1) { perror("socket"); return -1; } /* On veut remplir nous meme l'entete IP */ r=1; if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, (char *)&r, sizeof(r))==-1) { perror("setsockopt"); return -1; } memset(buffer, 0, 256); /* Debut de remplissage des champs de l'entete IP */ ip=(struct iphdr *)buffer; /* Recuperation de l'adresse IP de l'interface utilisee */ if (argc==2) { if (gethostname(recv_buffer, 255)==-1) { perror("gethostname"); return -1; } if ((host=gethostbyname(recv_buffer))==NULL) { perror("gethostbyname"); return -1; } memcpy(&ip->saddr, &host->h_addr_list[0], 4); } else { memset(&ifr, 0, sizeof(struct ifreq)); strcpy(ifr.ifr_name, argv[2]); if (ioctl(fd, SIOCGIFADDR, &ifr)==-1) { perror("ioctl"); return -1; } memcpy(&ip->saddr, (char *)ifr.ifr_addr.sa_data+2, 4); } /* IP header */ ip->version=4; /* IPv4 */ ip->ihl=5; /* 5 mots de 32 bits forment l'entete IP*/ ip->tos=0; /* Type de service */ ip->tot_len=htons(pktsize); /* Taille du paquet (header + data) */ ip->id=0x1234; /* Identifiant */ ip->frag_off=0; /* Pas de fragmentation */ ip->ttl=1; /* Van Jacobson rules */ ip->protocol=IPPROTO_ICMP; /* On envoie un ping */ ip->check=0; /* Pas obligatoire */ /* La machine de destination (en argument) peut etre une adresse IP... */ ip->daddr=inet_addr(argv[1]); printf("itrace %s ", inet_ntoa(*((struct in_addr *)&ip->saddr))); /* ...ou un nom d'hote, qu'il faut resoudre */ if (ip->daddr==-1) { if ((host=gethostbyname(argv[1]))==NULL) { perror("gethostbyname"); return -1; } memcpy(&ip->daddr, host->h_addr, 4); printf("> %s (%s)\n", inet_ntoa(*((struct in_addr *)&ip->daddr)), argv[1]); } else printf("> %s\n", inet_ntoa(*((struct in_addr *)&ip->daddr))); /* ICMP header */ icmp=(struct icmphdr *)(buffer+sizeof(struct iphdr)); icmp->type=ICMP_ECHO; /* Ping */ icmp->code=0; icmp->checksum=htons(~(ICMP_ECHO<<8)); /* Merci papasmurf.c */ /* Structure sockaddr_in pour sendto() et recvfrom() */ saddr_in.sin_family=AF_INET; saddr_in.sin_addr.s_addr=ip->daddr; ip_resp=(struct iphdr *)recv_buffer; icmp=(struct icmphdr *)(recv_buffer+sizeof(struct iphdr)); icmp->type=ICMP_TIME_EXCEEDED; /* Boucler tant qu'on recoit ce message d'erreur ICMP */ while (icmp->type==ICMP_TIME_EXCEEDED) { /* Envoi de notre paquet ICMP (demande d'echo) */ if (sendto(fd, buffer, pktsize, 0, (struct sockaddr *)&saddr_in, sizeof(struct sockaddr))<0) { perror("sendto"); return -1; } /* Heure à laquelle le paquet est parti */ gettimeofday(&tv_benchs, NULL); err=0; /* Capturer un message ICMP non local */ do { FD_ZERO(&fdsr); FD_SET(fd, &fdsr); /* On attend une réponse pendant 3 secondes */ tv_timeout.tv_sec=3; tv_timeout.tv_usec=0; /* Attente d'un message ICMP On ne peut pas appeler recvfrom() directement car cette fonction risque de bloquer indéfiniment */ if (select(fd+1, &fdsr, NULL, NULL, &tv_timeout)<=0) err=1; /* Données pretes sur la socket ? */ if (FD_ISSET(fd, &fdsr)) r=recvfrom(fd, recv_buffer, 256, 0, (struct sockaddr *)&saddr_in_from, &sin_len); else err=1; } while ((ip_resp->saddr==inet_addr("127.0.0.1") || ip_resp->saddr==ip->saddr)&&(err==0)); /* On a capturé un paquet ICMP non local */ if (err==0) { /* Heure de reception */ gettimeofday(&tv_benchr, NULL); printf("%2d %15s ", ip->ttl, inet_ntoa(*((struct in_addr *)&ip_resp->saddr))); /* Résolution dns */ if ((host=gethostbyaddr((char *)&ip_resp->saddr, 4, AF_INET))!=NULL) printf("%s", host->h_name); else printf("?"); printf(" %d ms\n", time_diff(&tv_benchs, &tv_benchr)); /* On incrémente le ttl pour que notre prochain paquet atteigne le routeur suivant */ ip->ttl++; } else { printf("%2d Timeout !\n", ip->ttl); icmp->type=ICMP_TIME_EXCEEDED; ip->ttl++; } } /* Opération terminée. Merci de votre attention */ close(fd); return 0; }