Le programme est décomposé en deux parties : la manipulation de trames Ethernet proprement dite et la manipulation de filtres de trames à la Berkeley.
/*********************************************/ /** Lecture et écriture de paquets Ethernet **/ /*********************************************/ /** Fichiers d'inclusion **/ #include <stdio.h> #include <errno.h> #include <unistd.h> #include <getopt.h> #include <time.h> #include <signal.h> #include <net/if.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/ioctl.h> #include <linux/in.h> #include <linux/if_ether.h> #include <linux/if_packet.h> #include <linux/filter.h> #include "bpf.h" /** Constantes **/ #define BUFFER_SIZE 2048 #define MAX_COLUMNS 16 #define ETHER_ADDR_LEN 6 #define MODE_DISPLAY 0 #define MODE_SEND 1 /** Variables globales **/ char *ifname="eth0"; /* Interface reseau a utiliser */ int mode=MODE_DISPLAY; /* Mode d'utilisation (lire ou ecrire) */ 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 */ /** Affichage de la syntaxe de la commande **/ void usage(char *name){ fprintf(stderr,"Usage: %s [OPTIONS]\n",name); fprintf(stderr," [[-i|--interface] <net interface>] (default %s)\n",ifname); fprintf(stderr," [-d|--display-mode] (default mode)\n"); fprintf(stderr," [-s|--send-mode]\n"); fprintf(stderr," [-b|--no-broadcast]\n"); fprintf(stderr," [[-u|--unicast] <mac address>]"); fprintf(stderr," (no restriction by default)\n"); exit(-1); } /** Recuperation des arguments **/ void parseOptions(int argc,char **argv) { int index; int c; /* Definition des options */ static char *options="i:dsbu:"; static const struct option longOptions[]={ {"interface",1,0,'i'}, {"display-mode",0,0,'d'}, {"send-mode",0,0,'s'}, {"no-broadcast",0,0,'b'}, {"unicast",1,0,'u'}, {0,0,0,0} } ; /* Analyse lexicale */ while((c=getopt_long(argc,argv,options,longOptions,&index))>0) switch(c){ case 'i': ifname=optarg; break; case 'd': mode=MODE_DISPLAY; break; case 's': mode=MODE_SEND; break; case 'b': broadcast=0; break; case 'u':{ int x[ETHER_ADDR_LEN]; int i,nb=sscanf(optarg,"%x:%x:%x:%x:%x:%x",x+0,x+1,x+2,x+3,x+4,x+5); if(nb!=ETHER_ADDR_LEN) usage(argv[0]); for(i=0;i<ETHER_ADDR_LEN;i++) macAddress[i]=x[i]; unicast=1; break; } default: usage(argv[0]); break; } if(optind<argc) usage(argv[0]); } /** Recherche du numero systeme d'une interface **/ int getInterfaceIndex(int sock,char *ifname){ struct ifreq ifr; strcpy(ifr.ifr_name,ifname); if(ioctl(sock,SIOCGIFINDEX,&ifr)<0){ perror("getInterfaceIndex.ioctl"); exit(-1); } return ifr.ifr_ifindex; } /** Passage d'une interface reseau en mode PROMISCUOUS **/ void setPromiscuousMode(int sock,char *ifname,unsigned char set){ static short int flags; struct ifreq ifr; strcpy(ifr.ifr_name,ifname); if(ioctl(sock,SIOCGIFFLAGS,&ifr)<0){ perror("setPromiscuousMode.ioctl(SIOCGIFFLAGS)"); exit(-1); } if(set==0) ifr.ifr_flags=flags; else{ flags=ifr.ifr_flags; ifr.ifr_flags|=IFF_PROMISC; } if(ioctl(sock,SIOCSIFFLAGS,&ifr)<0){ perror("setPromiscuousMode.ioctl(SIOCSIFFLAGS)"); exit(-1); } } /** Mise en place d'un filtre sur une interface reseau **/ void setFilter(int sock,char *ifname,struct sock_fprog *filter){ setsockopt(sock,SOL_SOCKET,SO_ATTACH_FILTER,filter,sizeof(struct sock_fprog)); } /** Ouverture de la socket **/ 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); } memset((void *)&address,0,sizeof(struct sockaddr_ll)); address.sll_family=AF_PACKET; address.sll_protocol=proto; address.sll_ifindex=getInterfaceIndex(result,ifname); if((bind(result,(struct sockaddr *)&address,sizeof(struct sockaddr_ll)))<0){ perror("openRawEthernetSocket.bind"); exit(1); } return result; } /** Adresse de la socket destination **/ void initTargetAddress(int sock,char *ifname,struct sockaddr_ll *address){ memset((void *)address,0,sizeof(struct sockaddr_ll)); address->sll_family=AF_PACKET; address->sll_halen=ETHER_ADDR_LEN; address->sll_ifindex=getInterfaceIndex(sock,ifname); } /** Lecture d'un paquet sur la socket **/ void readPacket(int sock,char *packet,int *size){ *size=recvfrom(sock,packet,*size,0,NULL,NULL); } /** Ecriture d'un paquet sur la socket **/ void writePacket(int sock,struct sockaddr_ll *address,char *packet,int size){ int asize=sizeof(struct sockaddr_ll); memcpy(address->sll_addr,packet,ETHER_ADDR_LEN); sendto(sock,packet,size,0,(struct sockaddr *)address,asize); } /** Affichage d'un paquet sur la sortie standard **/ 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"); } /** Lecture d'un paquet sur l'entree standard **/ void scanPacket(FILE *stream,char *packet,int *size){ int max=*size; int byte; *size=0; while(fscanf(stream,"%x",&byte)==1 && *size<max){ packet[*size]=(unsigned char)byte; (*size)++; } } /** Fonction principale **/ int sock; void clean(int signal){ if(unicast) setPromiscuousMode(sock,ifname,0); exit(0); } int main(int argc, char **argv){ struct sockaddr_ll address; int size; char buffer[BUFFER_SIZE]; parseOptions(argc,argv); sock=openRawEthernetSocket(ifname); if(mode==MODE_DISPLAY){ struct sock_fprog *filter=makeFilter(unicast?macAddress:NULL,broadcast); displayProgram(filter); setFilter(sock,ifname,filter); if(unicast) setPromiscuousMode(sock,ifname,1); signal(SIGINT,clean); while(1){ time_t t=time(NULL); size=BUFFER_SIZE; readPacket(sock,buffer,&size); fprintf(stdout,"Packet received at %s",ctime((const time_t *)&t)); displayPacket(stdout,buffer,size); fprintf(stdout,"\n"); } } else{ initTargetAddress(sock,"eth0",&address); size=BUFFER_SIZE; scanPacket(stdin,buffer,&size); writePacket(sock,&address,buffer,size); } exit(0); }
/***********************************************/ /** Gestion des filtres de capture de packets **/ /***********************************************/ /** Fichiers d'inclusion **/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <linux/types.h> #include <linux/filter.h> /** Constantes **/ #define MLCOND_ANY 0 #define MLCOND_UHOST 1 #define MLCOND_NUHST 2 #define MLCOND_BRDCS 4 #define MLCOND_NBRDC 8 /** Structures globales **/ /* Definition d'une instruction BPF */ typedef struct { char *label; /* Eventuel label de l'instruction */ char condition; /* Condition pour la compil. (MLCOND_XXXX) */ int code; /* Code de l'instruction */ char *trueLabel; /* Deplacement si vrai */ char *falseLabel; /* Deplacement si faux */ char *argument; /* Champ multi-usage */ } machineLanguage; /* Structure de stockage des valeurs des labels */ typedef struct { char *name; /* Nom du label */ int value; /* Numero de l'instruction associee */ } labelTable; /* Structure de stockage des valeurs des variables */ typedef struct { char *name; /* Nom de la variable */ long int value; /* Valeur de la variable */ } variableTable; /** Variables globales **/ machineLanguage filterProgram[]={ {NULL, MLCOND_ANY, BPF_LD|BPF_H|BPF_ABS, NULL, NULL, "12"}, {NULL, MLCOND_ANY, BPF_JMP|BPF_JEQ, "srcaddr", "ipv6", "0x800"}, {"ipv6", MLCOND_ANY, BPF_JMP|BPF_JEQ, "srcaddr", "arp", "0x86dd"}, {"arp", MLCOND_ANY, BPF_JMP|BPF_JEQ, "srcaddr", "fail", "0x806"}, {"srcaddr", MLCOND_UHOST, BPF_LD|BPF_W|BPF_ABS, NULL, NULL, "8"}, {NULL, MLCOND_UHOST, BPF_JMP|BPF_JEQ, "next2", "dstaddr", "MacLow"}, {"next2", MLCOND_UHOST, BPF_LD|BPF_H|BPF_ABS, NULL, NULL, "6"}, {NULL, MLCOND_UHOST, BPF_JMP|BPF_JEQ, "brdcast", "dstaddr", "MacHi"}, {"dstaddr", MLCOND_UHOST, BPF_LD|BPF_W|BPF_ABS, NULL, NULL, "2"}, {NULL, MLCOND_UHOST, BPF_JMP|BPF_JEQ, "next3", "fail", "MacLow"}, {"next3", MLCOND_UHOST, BPF_LD|BPF_H|BPF_ABS, NULL, NULL, "0"}, {NULL, MLCOND_UHOST, BPF_JMP|BPF_JEQ, "brdcast", "fail", "MacHi"}, {"brdcast", MLCOND_NBRDC, BPF_LD|BPF_W|BPF_ABS, NULL, NULL, "2"}, {NULL, MLCOND_NBRDC, BPF_JMP|BPF_JEQ, "next4", "success", "0xffffffff"}, {"next4", MLCOND_NBRDC, BPF_LD|BPF_H|BPF_ABS, NULL, NULL, "0"}, {NULL, MLCOND_NBRDC, BPF_JMP|BPF_JEQ, "fail", "success", "0xffff"}, {"success", MLCOND_ANY, BPF_LD|BPF_LEN, NULL, NULL, "0"}, {NULL, MLCOND_ANY, BPF_RET|BPF_A, NULL, NULL, "0"}, {"fail", MLCOND_ANY, BPF_RET|BPF_K, NULL, NULL, "0"}, {NULL,-1,0,NULL,NULL,0} }; /** Fonctions de calcul de tailles **/ int instNb(machineLanguage *program,int conditions){ int nb=0; while(program->condition>=0){ if((program->condition&conditions)==program->condition) nb++; program++; } return nb; } int labelNb(machineLanguage *program,int conditions){ int nb=0; while(program->condition>=0){ if(program->label!=NULL) nb++; program++; } return nb; } /** Fonction de calcul des valeurs de labels **/ labelTable *computeLabels(machineLanguage *program,int conditions){ int len=labelNb(program,conditions)+1; labelTable *result=(labelTable *)malloc(len*sizeof(labelTable)); int index=0,nb=0; while(program->condition>=0){ if(program->label!=NULL){ result[index].name=program->label; result[index++].value=nb; } if((program->condition&conditions)==program->condition) nb++; program++; } result[index].name=NULL; return result; } /** Trouve la valeur d'un label **/ int labelValue(labelTable *table,char *name){ if(name==NULL) return -1; while(table!=NULL&&table->name!=NULL){ if(strcmp(table->name,name)==0) return table->value; table++; } { int result=-1; if(sscanf(name,"0x%x",&result)!=1) sscanf(name,"%d",&result); return result; } } /** Trouve la valeur d'une variable **/ long int variableValue(variableTable *table,char *name){ if(name==NULL) return -1; while(table!=NULL&&table->name!=NULL){ if(strcmp(table->name,name)==0) return table->value; table++; } { long int result=-1; if(sscanf(name,"0x%lx",&result)!=1) sscanf(name,"%ld",&result); return result; } } /** Compile un programme **/ struct sock_fprog *compileProgram( machineLanguage *program,int conditions,variableTable *vtable){ labelTable *ltable=computeLabels(program,conditions); struct sock_fprog *result; int index=0,len=instNb(program,conditions); result=(struct sock_fprog *)malloc(sizeof(struct sock_fprog)); result->filter=(struct sock_filter *)malloc(len*sizeof(struct sock_filter)); result->len=len; while(program->condition>=0){ if((program->condition&conditions)==program->condition){ long int value=variableValue(vtable,program->argument); int jt=labelValue(ltable,program->trueLabel); int jf=labelValue(ltable,program->falseLabel); result->filter[index].code=program->code; result->filter[index].jt=jt<0?0:jt-index-1; result->filter[index].jf=jf<0?0:jf-index-1; result->filter[index].k=value; index++; } program++; } return result; } /** Interface avec le programme de lecture et d'ecriture de paquest Ethernet **/ struct sock_fprog *makeFilter(unsigned char *mac,unsigned char broadcast){ int flags=((mac==NULL)?MLCOND_NUHST:MLCOND_UHOST)| (broadcast?MLCOND_BRDCS:MLCOND_NBRDC); variableTable variables[3]; if(mac!=NULL){ variables[0].name="MacLow"; variables[0].value=(((((mac[2]<<8)|mac[3])<<8)|mac[4])<<8)|mac[5]; variables[1].name="MacHi"; variables[1].value=mac[0]<<8|mac[1]; variables[2].name=NULL; } else variables[0].name=NULL; return compileProgram(filterProgram,flags,variables); } /** Affiche un programme compile **/ void displayProgram(struct sock_fprog *program){ int i; printf("{\n"); for(i=0;i<program->len;i++){ struct sock_filter *block=program->filter+i; printf("/* %4d */ { 0x%x, %d, %d, 0x%x }%c\n",i, block->code,block->jt,block->jf,block->k,i<program->len-1?',':' '); } printf(" }\n"); }