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 :
-
construction théorique (sur papier) du paquet à émettre,
- émission réelle du paquet sur le réseau de Polytech'Lille,
- capture du ou des paquet(s) en réponse,
- 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 :
-
la somme de contrôle ne concerne que les entêtes du paquet IP
(pas les données du protocole encapsulé),
- pour le calcul, on considère que le champ de la somme de contrôle
est initialisé à zéro,
- on regroupe les octets deux par deux et en cas de nombre impair
d'octets on ajoute un octet nul,
- le calcul consiste à faire une addition en complément à 1 des mots
de 16 bits ainsi formés,
- 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.