IMA4 2021/2022 EC6

De Wiki d'activités IMA

Objectif

Développer un micro utilitaire de type tcpdump pour analyser les paquets TCP/IP.

En vous basant sur les sources du logiciel d'interception des paquets réseau des travaux dirigés du semestre 7, réalisez un analyseur de paquets TCP/IP avec les caractéristiques suivantes :

  • les paquets sont toujours affichés sous leur forme numérique ;
  • les entêtes Ethernet sont affichées en clair (les trois champs que vous connaissez bien) ;
  • si le paquet Ethernet contient un paquet IPv4, les entêtes IPv4 sont affichées en clair et la somme de contrôle est vérifiée ;
  • si le paquet IPv4 contient un paquet UDP, les entêtes UDP sont affichées en clair et l'éventuelle somme de contrôle est vérifiée.

Vous devez documenter vos avancées sur cette page Wiki. Au vu du retard que vous avez pris dans l'épreuve, merci de mettre à jour votre Wiki au moins deux fois par semaine.

Travail réaliser

Pour réaliser ce projet j'ai commencé par récupérer différents paquets ethernet "type" qui me permettront de développer mon programme . Pour cela j'utilise l'utilitaire wireshark Cela me permet de récupérer un exemple de trame ipv4 :

0000   48 f1 7f d0 32 20 78 81 02 31 36 76 86 dd 60 00   H...2 x..16v..`.
0010   00 00 00 21 11 3b 2a 00 14 50 40 07 08 0f 00 00   ...!.;*..P@.....
0020   00 00 00 00 20 02 2a 01 cb 00 0a a2 68 00 7d 7c   .... .*.....h.}|
0030   c5 0a 46 7f fe f6 01 bb d8 4c 00 21 0d c0 40 2e   ..F......L.!..@.
0040   40 71 fe 71 19 9e 41 f3 a7 a0 1c 35 5a 25 73 8b   @q.q..A....5Z%s.
0050   cf 41 11 bb 80 b3 b4                              .A.....

Ainsi que les informations associées:

Frame 680: 87 bytes on wire (696 bits), 87 bytes captured (696 bits) on interface \Device\NPF_{4DB062CB-0742-4EDE-86A1-1CFB3480F3BE}, id 0
Ethernet II, Src: Sercomm_31:36:76 (78:81:02:31:36:76), Dst: IntelCor_d0:32:20 (48:f1:7f:d0:32:20)
Internet Protocol Version 6, Src: 2a00:1450:4007:80f::2002, Dst: 2a01:cb00:aa2:6800:7d7c:c50a:467f:fef6
User Datagram Protocol, Src Port: 443, Dst Port: 55372
Data (25 bytes)

J'ai réalisé le même processus pour une trame UDP. Pour le moment ces trames sont stockées dans un fichier texte mais l'idéal est d'utiliser directement l'utilitaire ether pour récupérer un paquet J'ai décomposé mon programme en plusieurs routines. Récupération de la trame-> Récupération des en-têtes ethernet -> Sélection du type de protocole -> si c'est un IPV4 affichage de ses en-têtes et vérification de la checksum

Récupération de la trame: Ma première routine me permet de récupérer un paquet complet sous la forme d'un char Exemple 48 f1 7f d0 32 20 78 81 02 31 36 76 86 dd 60 00 00 00 00 21 11 3b 2a 00 14 50 40 07 08 0f 00 00 00 00 00 00 20 02 2a 01 cb 00 0a a2 68 00 7d 7c c5 0a 46 7f fe f6 01 bb d8 4c 00 21 0d c0 40 2e 40 71 fe 71 19 9e 41 f3 a7 a0 1c 35 5a 25 73 8b cf 41 11 bb 80 b3 b4

Je traite cette chaine de caractère avec des boucles for. Je sais que l'adresse mac source et de destination et source sont composées de 6 octets alors il me suffit de récupérer les octets correspondants que je mets dans une struct dédiée correspondant aux en-têtes ethernet.

Documents Rendus

  1. include <stdio.h>
  2. include <stdlib.h>
  3. include <string.h>
  4. include <sys/stat.h>
  5. define DEFAUT_SIZE 300


  1. include <errno.h>
  2. include <unistd.h>
  3. include <getopt.h>
  4. include <time.h>
  5. include <signal.h>
  6. include <net/if.h>
  7. include <sys/socket.h>
  8. include <sys/types.h>
  9. include <sys/ioctl.h>
  10. include <linux/in.h>
  11. include <linux/if_ether.h>
  12. include <linux/if_packet.h>
  13. include <linux/filter.h>
  1. define BUFFER_SIZE 2048
  2. define MAX_COLUMNS 16
  3. define ETHER_ADDR_LEN 6
  1. define MODE_DISPLAY 0
  2. define MODE_SEND 1


struct etherFrame {

   char sourceMac[18];
   char destMac[18];
   char etherType[5];
   char nameType[15];
   int casePacket;
  
   

};

struct ipFrame{

   char versionIp[2];
   char lenghtHeaderIp[2];
   char DSCP_ECN[2];
   char lenghtIP[4];
   char identIp[5];
   char flagIp[3];
   char TTL[5];
   char protocolIP[3];
   char CsIP[5];
   unsigned char sourceIP[4];
   unsigned char destIP[4];
   char fullHeader[40];

};

const char* filename = "temp.txt"; int indexPacket=0;


char *ifname="enp0s3"; /* Interface reseau a utiliser */ unsigned char broadcast=1; /* Capture ou non des diffusions */ unsigned char unicast=0; /* Capture limitee a une adresse MAC ou non */ char macAddress[ETHER_ADDR_LEN];/* adresse MAC a surveiller */


int openRawEthernetSocket(char *ifname){

 struct sockaddr_ll address;
 int proto=htons(ETH_P_ALL);
 int result;
 if((result=socket(PF_PACKET,SOCK_RAW,proto))<0){
   perror("openRawEthernetSocket.socket");
   exit(1);
 }
 return result;
 }


 void readPacket(int sock,char *packet,int *size){
 *size=recvfrom(sock,packet,*size,0,NULL,NULL);
 }

int get_packet(char *packet, char *buffer, int size){

int i,j; j=0;

//packet=(char *)malloc(sizeof(char)*size); for (i=0;i<size;i++){ sprintf(&packet[j],"%02x",(unsigned char)buffer[i]); j=j+2; } packet[j]='\0';

   }
   

struct etherFrame getMacAddressType(char *packetChar){

   struct etherFrame ether1;
   int i;
   // nettoyage de la chaîne de charactère à cause d'un bug :"G��U48:f1:7f:f0:32:20:" en plus de la mac address des charactère sont présents au début 
   // Je pense que l'erreur ici est que j'ai alloué une zone mémoire à ma string mais je ne l'ai pas initialisé 
   strcpy(ether1.destMac,"");
   strcpy(ether1.sourceMac,"");
   strcpy(ether1.etherType,"");
   strcpy(ether1.nameType,"");
   // Pour résoudre ça je copie dans ma string un texte vide 

// Récupération de l'addresse mac de destination

   for(i=0;i<12;i=i+2){
       strcat(ether1.destMac,":"); 
       strncat(ether1.destMac,&packetChar[i],1);
       strncat(ether1.destMac,&packetChar[i+1],1);
              
   };
   printf("\nAddresse mac de destination%s\n",ether1.destMac);

//Récupération de l'addresse mac source

   for(i=12;i<24;i=i+2){
       strcat(ether1.sourceMac,":");
       strncat(ether1.sourceMac,&packetChar[i],1);
       strncat(ether1.sourceMac,&packetChar[i+1],1);
       
   }
   printf("Addresse mac source%s\n",ether1.sourceMac);
   ;// bug de mémoire des caractères sont ajoutés sans raison (solved)
   
   

// Récupération du type

   *(ether1.etherType)=*(packetChar+24);
   *(ether1.etherType+1)=*(packetChar+25);
   *(ether1.etherType+2)=*(packetChar+26);
   *(ether1.etherType+3)=*(packetChar+27);
   
   // J'avais ici une erreur dans ma string. Lorsque je rajoutais le nameType la char etherType "récupérait" aussi la valeur IPV4 ce qui me rendait printf(etherType) 0800IPV4
   // La lenght de etherType changeait aussi sans raison
   // En utilisant autre chose qu'une fonction toute faite comme strcat ou strcpy je n'ai pas ajouté \0 à la fin de ma string. etherType et nameType sont collés dans la mémoire. Alors sans \0 la mémoire considérait que
   // etherType s'arretait à \0 de nameType
   *(ether1.etherType+4)='\0';
   
   // Vérification du type etherType
   if (strcmp(ether1.etherType,"0800")==0){
       strcpy(ether1.nameType,"IPV4");
       ether1.casePacket =1;
           }
   else if (strcmp(ether1.etherType,"0806")==0){
       strcpy(ether1.nameType,"ARP");  
       ether1.casePacket =2;
   }
   else if (strcmp(ether1.etherType,"86dd")==0){
       strcpy(ether1.nameType,"IPV6");        
       ether1.casePacket =3;
   }
   else {
       strcpy(ether1.nameType,"UNKNOWNS");
   }
   printf("Type en Hexa: %s/ EtherType:%s\n",ether1.etherType,ether1.nameType);
   return ether1;

}


struct ipFrame analyseIP4(struct etherFrame ether1, char *packetChar){

   struct ipFrame ip;
   // récupération de la version IP et de la longueur de l'en-tête
   //printf("Test");
   char *ptr;
   strcpy(ip.versionIp,"");
   strcpy(ip.lenghtHeaderIp,"");
   strcpy(ip.DSCP_ECN,"");
   strcpy(ip.lenghtIP,"");
   strcpy(ip.identIp,"");
   ip.versionIp[0]=*(packetChar+28);
   ip.lenghtHeaderIp[0]=*(packetChar+29);
   int tailleHeader = atoi(ip.lenghtHeaderIp)*4;
   printf("\nTaille Header %d byte",tailleHeader);
   
   ip.DSCP_ECN[0]=*(packetChar+30);
   ip.DSCP_ECN[1]=*(packetChar+31);
   
   ip.lenghtIP[0]=*(packetChar+32);
   ip.lenghtIP[1]=*(packetChar+33);
   ip.lenghtIP[2]=*(packetChar+34);
   ip.lenghtIP[3]=*(packetChar+35);
   
   //printf("%s",ip.lenghtIP);
   long taillePaquetIp=strtol(ip.lenghtIP,NULL,16);
   printf("\nTaille du paquet IP: %ld",taillePaquetIp);
   
   ip.identIp[0]=*(packetChar+36);
   ip.identIp[1]=*(packetChar+37);
   ip.identIp[2]=*(packetChar+38);
   ip.identIp[3]=*(packetChar+39);
   ip.identIp[4]='\0';
   printf("\nIdentification %s",ip.identIp);
   
   ip.flagIp[0]=*(packetChar+40);
   ip.flagIp[1]=*(packetChar+41);
   ip.flagIp[2]='\0';
   printf("\nFlag IP: %s", ip.flagIp);
   
   ip.TTL[0]=*(packetChar+42);
   ip.TTL[1]=*(packetChar+43);
   ip.TTL[2]=*(packetChar+44);
   ip.TTL[3]=*(packetChar+45);
   ip.TTL[4]='\0';
   printf("\nTTL: %s", ip.TTL);
   
   ip.protocolIP[0]=*(packetChar+46);
   ip.protocolIP[1]=*(packetChar+47);
   ip.protocolIP[2]='\0';
   printf("\nProtocole:%s", ip.protocolIP);
   ;
   ip.CsIP[0]=*(packetChar+48);
   ip.CsIP[1]=*(packetChar+49);
   ip.CsIP[2]=*(packetChar+50);
   ip.CsIP[3]=*(packetChar+51);
   ip.CsIP[4]='\0';
   printf("\nCS= %s",ip.CsIP);
   
   // Récupération de l'ip source
   char tempChar[2];    
   tempChar[2]='\0';
   tempChar[0]=*(packetChar+52);
   tempChar[1]=*(packetChar+53);
   ip.sourceIP[0]= strtol(tempChar,NULL,16);
   
   tempChar[0]=*(packetChar+54);
   tempChar[1]=*(packetChar+55);
   ip.sourceIP[1]=strtol(tempChar,NULL,16);
   
   tempChar[0]=*(packetChar+56);
   tempChar[1]=*(packetChar+57);
   ip.sourceIP[2]= strtol(tempChar,NULL,16);
   
   tempChar[0]=*(packetChar+58);
   tempChar[1]=*(packetChar+59);
   ip.sourceIP[3]= strtol(tempChar,NULL,16);
   
   //Récupération de l'ip dest
   tempChar[0]=*(packetChar+60);
   tempChar[1]=*(packetChar+61);
   ip.destIP[0]= strtol(tempChar,NULL,16);
   
   tempChar[0]=*(packetChar+62);
   tempChar[1]=*(packetChar+63);
   ip.destIP[1]= strtol(tempChar,NULL,16);
   
   tempChar[0]=*(packetChar+64);
   tempChar[1]=*(packetChar+65);
   ip.destIP[2]= strtol(tempChar,NULL,16);
   
   tempChar[0]=*(packetChar+66);
   
   tempChar[1]=*(packetChar+67);
   ip.destIP[3]= strtol(tempChar,NULL,16);
   
   printf("\nIp source %d.%d.%d.%d",ip.sourceIP[0],ip.sourceIP[1],ip.sourceIP[2],ip.sourceIP[3]);
   printf("\nIp de destination %d.%d.%d.%d",ip.destIP[0],ip.destIP[1],ip.destIP[2],ip.destIP[3]);
   

// printf("tes 1 %d\n",strlen(ip.fullHeader)); // printf("test 2 %d\n",sizeof(ip.fullHeader)); //

   printf("\n%d ", tailleHeader);

// ip.fullHeader = malloc(tailleHeader); // printf("test 3 %d\n",strlen(ip.fullHeader)); // printf("test 4 %d\n",sizeof(ip.fullHeader));

   int index=0;
   int i;
   
   for(i=28;i<28+(tailleHeader*2);i=i+1){

// printf("%d\n",strlen(ip.fullHeader)); // printf("%s",ip.fullHeader);

       ip.fullHeader[index]=packetChar[i];
       index++;
   }
   ip.fullHeader[tailleHeader*2]='\0';
   //printf("\n%s",ip.fullHeader);
   return ip;

} void displayPacket(FILE *stream,char *packet,int size){

 int i,count=MAX_COLUMNS;
 for(i=0;i<size;i++){
   fprintf(stream,"%2.2x ",(unsigned char)packet[i]);
   count--;
   if(count<=0){ fprintf(stream,"\n"); count=MAX_COLUMNS; }
   }
 if(count!=MAX_COLUMNS) fprintf(stream,"\n");
 }
   
void checksum(char *header, char *inputCS){
	char byte1[]="0000000000000000";
	char byte2[]="0000000000000000";
	char word[]="0000";
	char wordBinary[]="0000000000000000";
	char checkResult[]="0000000000000000";
	char carry[]="0";
	int len=strlen(header);
	
	int i=0;
	int j,k;
	byte2[17]='\0';
	
	while (i<len){

sprintf(word,"%.4s", header+i); // récupération des 2 octets

if(strcmp(word,inputCS)==0){ strcpy(word,"0000"); strcpy(wordBinary,"0000000000000000"); strcpy(byte1,"0000000000000000"); } // exclusion de la CS du header

else{ strcpy(byte1,"0000000000000000"); sprintf(wordBinary,"%b",strtol(word,NULL,16)); k=16;


for (j=strlen(wordBinary); j!=-1; j=j-1){ byte1[k]=wordBinary[j]; k=k-1; } }

// addiftion de byte1 et byte2 for (j=15;j!=-1;j--){ if (byte1[j]=='0' && byte2[j]=='0' && carry[0]=='0') { byte2[j]='0'; } else if (byte1[j]=='0' && byte2[j]=='0'&& carry[0]=='1'){ byte2[j]='1'; carry[0]='0';} else if (byte1[j]=='0' && byte2[j]=='1' && carry[0]=='0'){ byte2[j]='1'; carry[0]='0';} else if (byte1[j]=='1' && byte2[j]=='0' && carry[0]=='0'){ byte2[j]='1'; carry[0]='0';} else if (byte1[j]=='1' && byte2[j]=='1' && carry[0]=='0'){ byte2[j]='0'; carry[0]='1';} else if (byte1[j]=='1' && byte2[j]=='1' && carry[0]=='1'){ byte2[j]='1'; carry[0]='1';}

}

/** printf("\nHexa %s",word); printf("\nWord: %s",byte1); printf("\nCS:  %s\n",byte2); */ i=i+4;

	}
	// complément à 1 du résultat et affichage en hex
	//printf("\nChecksum calculé: %lx \n",strtol(byte2,NULL,2));
	for (j=15;j>0;j--){

if (byte2[j]=='0' ){ checkResult[j]='0'; } else { checkResult[j]='1'; } }

printf("\nChecksum calculé: %lx \n",strtol(checkResult,NULL,2));


} 
  

void main(){

int sock;

 	int size;
 	char buffer[BUFFER_SIZE];  	
 	sock=openRawEthernetSocket(ifname);
 	readPacket(sock,buffer,&size);
 	
 	
 	if (strlen(buffer)==0){printf("Aucun paquet reçu\n");exit(0);};
 	printf("\nAffichage du paquet:\n");
  	displayPacket(stdout,buffer,size);
  	
  	
  	char packetChar[2+size*2];

get_packet(packetChar,buffer,size); //printf("\n%s", packetChar); struct etherFrame ether2; ether2=getMacAddressType(packetChar);

   	switch(ether2.casePacket){
       case 1:
           printf("\nCase IPV4:");
           struct ipFrame resultIP= analyseIP4(ether2, packetChar);
           int caseIp = atoi(resultIP.protocolIP);
           
           switch(caseIp){
               case 6:
                   printf("\nTCP:\n");
                   // Case TCP 
                   checksum(resultIP.fullHeader, resultIP.CsIP);
                   ;
                   break;
               case 17:
                   // UDP 
                   printf("\nUDP\n");
                   checksum(resultIP.fullHeader, resultIP.CsIP);
           ;
           break;
   }}


}