Previous Contents Next

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 <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");
  }

Previous Contents Next