A Sources du programme de manipulation de trames Ethernet
Le programme est décomposé en deux parties : la manipulation de trames
Ethernet proprement dite et la manipulation de filtres de trames
à la Berkeley.
A.1 Fichier concernant la manipulation de trames Ethernet
/*********************************************/
/** 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);
}
A.2 Fichier concernant la manipulation de filtres pour les trames
/***********************************************/
/** Gestion des filtres de capture de packets **/
/***********************************************/
/** Fichiers d'inclusion **/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.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_NUHST,
BPF_JMP|BPF_JEQ, "success", "next1", "0x800"},
{NULL, MLCOND_UHOST,
BPF_JMP|BPF_JEQ, "srcaddr", "next1", "0x800"},
{"next1", MLCOND_NUHST,
BPF_JMP|BPF_JEQ, "success", "fail", "0x806"},
{"next1", MLCOND_UHOST,
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, "success", "dstaddr", "MacHi"},
{"dstaddr", MLCOND_UHOST,
BPF_LD|BPF_W|BPF_ABS, NULL, NULL, "2"},
{NULL, MLCOND_UHOST|MLCOND_NBRDC,
BPF_JMP|BPF_JEQ, "next3", "fail", "MacLow"},
{NULL, MLCOND_UHOST|MLCOND_BRDCS,
BPF_JMP|BPF_JEQ, "next3", "brdcast", "MacLow"},
{"next3", MLCOND_UHOST,
BPF_LD|BPF_H|BPF_ABS, NULL, NULL, "0"},
{NULL, MLCOND_UHOST,
BPF_JMP|BPF_JEQ, "success", "fail", "MacHi"},
{"brdcast", MLCOND_UHOST|MLCOND_BRDCS,
BPF_LD|BPF_W|BPF_ABS, NULL, NULL, "2"},
{NULL, MLCOND_UHOST|MLCOND_BRDCS,
BPF_JMP|BPF_JEQ, "next4", "fail", "0xffffffff"},
{"next4", MLCOND_UHOST|MLCOND_BRDCS,
BPF_LD|BPF_H|BPF_ABS, NULL, NULL, "0"},
{NULL, MLCOND_UHOST|MLCOND_BRDCS,
BPF_JMP|BPF_JEQ, "success", "fail", "0xffff"},
{"fail", MLCOND_ANY,
BPF_RET|BPF_K, NULL, NULL, "0"},
{"success", MLCOND_ANY,
BPF_LD|BPF_LEN, NULL, NULL, "0"},
{NULL, MLCOND_ANY,
BPF_RET|BPF_A, 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->condition&conditions)==program->condition &&
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->condition&conditions)==program->condition){
if(program->label!=NULL){
result[index].name=program->label;
result[index++].value=nb;
}
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");
}