Travaux pratiques de réseau (GIS quatrième année)
Analyse de paquets TCP/IP

Xavier Redon & Thomas Vantroys

1   Introduction

Le but de ce TD est de forger et de décrypter des paquets de la suite de protocoles réseau TCP/IP.

1.1   Déroulement du TD

Chaque exercice doit être abordé suivant les étapes ci-dessous :
  1. construction théorique (sur papier) du paquet à émettre,
  2. émission réelle du paquet sur le réseau de Polytech'Lille,
  3. capture du ou des paquet(s) en réponse,
  4. décryptage (sur papier) de la réponse.

1.2   Détails techniques

Pour vous permettre d'émettre et de recevoir des paquets Ethernet sur le réseau de Polytech'Lille, vous disposez d'un utilitaire ether (le binaire se trouve dans /usr/local/bin et vous pouvez trouver les sources complètes dans /usr/local/src/LSF).

1.3   Indications pratiques

L'utilitaire ether est utilisable dans deux modes différents : un mode d'affichage où il affiche les paquets Ethernet reçus par l'interface réseau et un mode pour envoyer un paquet Ethernet. Vous laisserez donc une instance du programme tourner en mode affichage et vous en utiliserez une autre pour envoyer vos propres paquets. En mode affichage vous pouvez demander à l'utilitaire de n'afficher que les paquets à destination d'une certaine adresse Ethernet, vous pouvez même demander à ne pas afficher les paquets de diffusion (broadcast). Tapez ether --help pour obtenir la syntaxe des différentes options. Essayez d'utiliser directement le programme ether. Que se passe-t-il ? Par la suite vous utiliserez le programme en passant par la commande super (regardez la page de manuel de super et son fichier de configuration).

2   Initiation à l'utilisation du programme ether

Cet exercice a pour objectif de vous familiariser avec l'utilitaire ether. Pour cela nous allons réaliser un petit utilitaire de conversation instantanée.

2.1   Envoi manuel

Déterminez l'adresse Ethernet de la machine d'un autre binôme. Forgez et envoyez ensuite une trame Ethernet à destination de cette machine (utilisez le type Ethernet 1111). Assurez vous que la trame a bien été reçue.

2.2   Utilitaire de conversation

Utilisez les squelettes de scripts PHP fournis en annexe pour réaliser deux commandes : une pour recevoir et afficher les messages envoyés avec le type 1111 et une pour envoyer un message à une machine donnée. Les messages doivent être donnés en clair (une chaine de caractères) et être affichés en clair.

3   Vérification du fonctionnement des commutateurs

Cette fois envoyez une trame Ethernet de type 1111 à destination d'une adresse Ethernet que vous savez inutilisée sur le réseau local (et avec une adresse Ethernet source elle aussi inutilisée sur le réseau local). Que constatez-vous ? Associez-vous avec un autre binôme, envoyez une réponse à leur paquet Ethernet pendant qu'ils font de même avec le vôtre. Envoyez à nouveau votre paquet initial, que constatez-vous ?

4   Paquets ARP

Cet exercice est une manipulation du protocole ARP de résolution d'adresses MAC de la suite TCP/IP.

4.1   Envoi d'un paquet ARP

Forgez un paquet de demande de résolution d'adresse MAC pour une adresse IP utilisée sur le réseau des étudiants de Polytech'Lille. Faites en sorte que la réponse puisse être aperçue sur l'interface Ethernet de votre machine. Envoyez le paquet, capturez la réponse et décodez la.

4.2   Réponse à un paquet ARP

Utilisez la commande ping pour tester la présence d'une machine sur le réseau étudiant. Prenez une adresse IP dont vous êtes sûr qu'elle n'est pas utilisée. Vérifiez qu'une question ARP est envoyée sur le réseau pour trouver l'adresse MAC correspondant à cette adresse IP. Capturez cette demande et forgez le paquet de réponse. Envoyez la réponse sur le réseau. Quel type de paquet est ensuite envoyé à l'adresse MAC que vous avez prétendu correspondre à l'adresse IP ?

5   Paquets IP

5.1   Indications pratiques

Dans les entêtes d'un paquet IP on trouve une somme de contrôle. Le calcul de la somme de contrôle est décrit dans la RFC 1071. En résumé, le calcul se fait comme suit :
  1. la somme de contrôle ne concerne que les entêtes du paquet IP (pas les données du protocole encapsulé),
  2. pour le calcul, on considère que le champ de la somme de contrôle est initialisé à zéro,
  3. on regroupe les octets deux par deux et en cas de nombre impair d'octets on ajoute un octet nul,
  4. le calcul consiste à faire une addition en complément à 1 des mots de 16 bits ainsi formés,
  5. on met dans le champ de la somme de contrôle non pas le résultat de l'addition mais l'inverse (au sens de la négation booléenne bit à bit).
Ecrivez un petit programme pour calculer une somme de contrôle IP, vous pouvez vous aider du morceau de code C de la RFC 1071.

5.2   Somme de contrôle

Forgez un paquet IP encapsulant un paquet vide d'un protocole IP fantaisiste. Commencez par mettre une somme de contrôle non adaptée. Envoyez le paquet, voyez-vous passer une réponse, pourquoi ? Modifiez le paquet en insérant une somme de contrôle correcte, envoyez-le. Repérez un paquet en réception en relation avec le paquet envoyé. Forgez la réponse à ce dernier paquet. Envoyez ce paquet réponse, décodez la suite de l'échange.

5.3   Durée de vie

Forgez un paquet IP encapsulant un paquet vide d'un protocole IP fantaisiste. Ce paquet doit être à destination d'une machine sur le réseau serveurs de Polytech'Lille. Positionnez le champ TTL à 1. Envoyez le paquet, faites ce qu'il faut pour qu'un paquet ICMP vous parvienne. Que signifie ce paquet ICMP ?

6   Paquets ICMP

6.1   Indications pratiques

La somme de contrôle d'un paquet ICMP tient compte des octets de données (i.e. l'entête du paquet ayant donné lieu à l'emission du paquet ICMP).

6.2   Paquets de type echo

Reprenez l'exercice sur ARP manipulant la commande ping. Faites en sorte de répondre au paquet ICMP de demande d'écho (forgez le paquet, envoyez le, vérifiez au niveau de la commande ping que votre réponse est bien comprise).

7   Paquets ICMPv6

7.1   Indications pratiques

Une différence majeure entre IPv4 et IPv6 réside dans la somme de contrôle qui a disparu du niveau IP. Il faut cependant s'assurer que les paquets transmis ne comportent pas d'erreurs, notamment sur les adresses source et destination. La somme de contrôle de ICMPv6 est calculée à partir d'un pseudo-en-tête (visible sur schéma ci-dessous) concaténé avec le paquet ICMP complet. L'algorithme de calcul est le même que pour IPv4.

7.2   Paquets de type echo

Utilisez l'utilitaire ping6 (équivalent en IPv6 de l'utilitaire ping) pour tester la présence d'une machine sur le réseau étudiant. Prenez une adresse IPv6 2001:660:4401:6004:xxxx:xxxx:xxxx:xxxx, construite à l'aide de l'EUI-64 correspondant à votre adresse MAC virtuelle. Forgez le paquet de réponse à la demande de résolution d'adresse (rappel : le protocole ARP est intégré dans le protocole ICMP en IPv6). Puis envoyez un paquet de réponse d'écho.

8   Paquets UDP

8.1   Indications pratiques

La somme de contrôle d'un paquet UDP tient compte d'une pseudo-entête IP (voir schéma ci-dessous), de l'entête UDP (avec le champ somme de contrôle initialisé à zéro) et des données UDP.
Si la somme calculée vaut 0x0000 il faut envoyer 0xffff (la valeur zéro représente un paquet UDP sans somme de contrôle).

8.2   Emulation de la commande traceroute

Envoyez des paquets UDP vous permettant de connaitre la liste des routeurs sur le chemin entre votre machine de TP et la machine guillotin.prism.uvsq.fr.

9   Paquets TCP

9.1   Indications pratiques

La somme de contrôle d'un paquet TCP tient compte d'une pseudo-entête IP (voir schéma ci-dessous), de l'entête TCP (avec le champ somme de contrôle initialisé à zéro) et des données TCP.
Le champ taille du paquet TCP dans la pseudo entête IP doit être calculée à partir de la taille du paquet IP et de la taille des entêtes IP.

9.2   Refus de connexion par RESET

Logez-vous sur une autre machine de TP, utilisez la commande nc pour vous connecter sur un port de la machine que vous émulez. Répondez à la requête ARP générée par nc. Étudiez le paquet TCP de demande de connexion, répondez par un paquet TCP RESET.

9.3   Émulation de la phase de connexion

Reprenez la question précédente mais cette fois envoyez un paquet de réponse SYN. Vérifiez par la commande netstat que la connexion TCP est bien établie.

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

B   Squelettes des programmes pour le chat Ethernet

On vous donne le squelette pour l'émetteur et le récepteur.

B.1   Fichier pour l'émetteur

#!/usr/bin/php
<?
define('IFCONFIG','/sbin/ifconfig');
define('HW_KEYWORD','HWaddr');
define('IFNAME','eth0');
define('ETHER','super ether -s');
define('TYPE','11 11');

function getMacAddress($if){
$cmd=IFCONFIG." $if";
$df=popen($cmd,'r');
while(!feof($df)){
  $buffer=fgets($df);
  $address=ereg_replace('.*'.HW_KEYWORD.'[ :]*([0-9A-Fa-f:]*).*','\\1',$buffer);
  if($address) return $address;
  }
fclose($df);
return $address;
}

function dec2hexa($tab){
$tab_hex=array();
foreach($tab as $dec) $tab_hex[]=sprintf("%02x",$dec);
return implode(' ',$tab_hex);
}

function mac2hexa($mac){
$tab_str=explode(':',$mac);
$tab_dec=array();
foreach($tab_str as $str){ list($dec)=sscanf($str,"%x"); $tab_dec[]=$dec; }
return dec2hexa($tab_dec);
}

function sendPacket($from,$to,$type,$mess){
$packet = ...
$df=popen(ETHER,'w');
fputs($df,"$packet\n");
fclose($df);
}

if($argc!=3){
  echo "Usage: ".basename($argv[0])." <target MAC> <message>\n";
  exit(-1);
  }

$from=getMacAddress(IFNAME);
$to=$argv[1];
$mess=$argv[2];

...
?>

B.2   Fichier pour le récepteur

#!/usr/bin/php
<?
define('ETHER','super ether');
define('ETHER_KEYWORD','Packet');

function displayPacket($bytes){
...
}

function readPackets(){
$state='headers'; $bytes="";
$df=popen(ETHER,'r');
while(!feof($df)){
  $buffer=fgets($df);
  if(eregi(ETHER_KEYWORD,$buffer)){
    if($state=='packet'){
      $tab=explode(' ',trim($bytes));
      displayPacket($tab);
      $bytes="";
      continue;
      }
    else $state='packet';
    }
  if($bytes) $bytes .= " ";
  $bytes .= trim($buffer);
  }
fclose($df);
}

readPackets();
?>

This document was translated from LATEX by HEVEA.