Tutorat de programmation réseau
(Polytech'Lille, département GIS, quatrième année).
Réalisation d'un client RIP pour IPv4
Thomas Vantroys & Xavier Redon
1 Objectif du TP
Écrire un programme en C permettant de récupérer les paquets RIP v1
envoyés par des routeurs sur le réseau local. Ce programme doit aussi
être capable d'avertir une route, c'est à dire d'envoyer un paquet RIP v1
pour dire aux routeurs sur le réseau qu'il est routeur pour un réseau IP
donné.
2 Exemple d'utilisation du programme
Un exemple d'utilisation de votre programme en mode "affichage" est :
rip --display
Un exemple d'utilisation de votre programme en mode "diffusion" est :
rip --network 192.168.2.0 --metric 1
3 Indications
Votre programme utilisera la bibliothèque des sockets, en particulier
la famille de sockets PF_INET et le mode non connecté SOCK_DGRAM.
Le protocole RIP v1 est défini par la RFC 1058, utilisez la pour comprendre
le protocole. Le port UDP utilisé par RIP est le port 520.
4 Mode affichage
Votre programme fonctionne avec des options (comme --display), vous
allez donc utiliser la fonction de bibliothèque getopt_long.
Le routeur de l'école envoye périodiquement des paquets RIP en diffusion
totale sur le port UDP 520. Essayez de lier une socket UDP sur ce port.
Que constatez-vous ? Pour résoudre votre problème, regardez le programme
C donné en annexe et le fichier /etc/super.tab.
Arrivez-vous à recevoir les paquets UDP envoyé en diffusion totale ?
Si non, regardez du coté de l'option socket (niveau SOL_SOCKET)
de nom SO_BROADCAST.
Pour analyser les datagrammes RIP, utilisez la structure struct rip
définie dans le fichier d'entêtes routed.h. Pour remplir la structure
utilisez des adresses de socket Internet (struct sockaddr_in); elles
sont plus adaptées que les adresses de sockets génériques.
5 Mode diffusion
Tentez d'envoyer votre paquet UDP directement. Cela fonctionne-t-il ? Pour quelle
raison (cherchez dans la RFC) ? Comment pouvez-vous procéder ?
Testez la bonne prise en compte de la route en utilisant ping pour sonder
une adresse IP du réseau de destination.
A Programme répéteur UDP
/** Small utility to listen for UDP packets on an UDP port **/
/** and repeat them on another UDP port **/
/** Include files **/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <netdb.h>
#include <string.h>
/** Constants definitions **/
#define ACTIVITY_DELAY 60
#define MAX_BUFFER 2048
#define PORT_LISTEN 520
#define PORT_REPEAT 5200
/** General types definitions **/
/** Functions **/
/* Create a socket on a given UDP port */
int create_socket(short int port,unsigned char use_bdcst){
struct sockaddr_in address;
int status,broadcast=1;
int sd=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(sd<0){
perror("create_socket.socket");
exit(-1);
}
address.sin_family=AF_INET;
address.sin_port=htons(port);
address.sin_addr.s_addr=INADDR_ANY;
if(use_bdcst){
status=setsockopt(sd,SOL_SOCKET,SO_BROADCAST,&broadcast,sizeof broadcast);
if(status<0) return -1;
}
status=bind(sd,(struct sockaddr *)&address,sizeof address);
if(status<0){perror("create_socket.bind"); exit(-1);}
return sd;
}
/* Wait for activity on a given sockets */
int wait_activity(int *sds,int *ready,int nb){
fd_set read;
struct timeval tv;
int status,i,max=0;
int activity=-1;
FD_ZERO(&read);
for(i=0;i<nb;i++){
FD_SET(sds[i],&read);
if(sds[i]>max) max=sds[i];
}
tv.tv_sec=ACTIVITY_DELAY; tv.tv_usec=0;
status=select(max+1,&read,NULL,NULL,&tv);
if(status<0){
perror("wait_activity.select");
exit(-1);
}
for(i=0;i<nb;i++)
if(!FD_ISSET(sds[i],&read)) ready[i]=0;
else { activity=1; ready[i]=1; }
return activity;
}
/* Read a packet on one socket and send it on the other */
void repeat_packet(int sd_recv,int sd_send,int port_send){
struct sockaddr_in address_sender;
struct sockaddr_in address_local;
unsigned char buffer[MAX_BUFFER];
int status,packet_size,sent_size;
unsigned int size;
size=sizeof address_local;
status=getsockname(sd_recv,(struct sockaddr *)&address_local,&size);
if(status<0){ perror("repeat_packet.getsockname"); exit(-1); }
packet_size=recvfrom(sd_recv,buffer,MAX_BUFFER,0,
(struct sockaddr *)&address_sender,&size);
if(address_local.sin_port!=address_sender.sin_port) return;
size=sizeof address_local;
status=getsockname(sd_send,(struct sockaddr *)&address_local,&size);
if(status<0){ perror("repeat_packet.getsockname"); exit(-1); }
address_local.sin_port=htons(port_send);
address_local.sin_addr.s_addr=INADDR_BROADCAST;
size=sizeof address_local;
sent_size=sendto(sd_send,buffer,packet_size,0,
(struct sockaddr *)&address_local,size);
if(sent_size!=packet_size){
fprintf(stderr,"Cannot repeat UDP packet.\n");
exit(-1);
}
}
/* Main function */
int main(int argc,char *argv[]){
int listen_port,repeat_port;
int sds[2],ready[2];
/* Analyze arguments */
if(argc==3){
listen_port=atoi(argv[1]);
repeat_port=atoi(argv[2]);
}
else{
listen_port=PORT_LISTEN;
repeat_port=PORT_REPEAT;
}
fprintf(stderr,"Listening on UDP port %d, repeating on UDP port %d.\n",listen_port,repeat_port);
/* Wait for activity on repeating port */
fprintf(stderr,"Listening on UDP port %d for activity.\n",listen_port);
sds[0]=create_socket(repeat_port,1);
if(sds[0]<0){
fprintf(stderr,"Cannot bind to port %d.\n",repeat_port);
exit(-1);
}
if(wait_activity(sds,ready,1)==1){
fprintf(stderr,"Found activity on port %d, exiting.\n",repeat_port);
exit(-1);
}
/* Wait for packet on a port and repeat on the other */
fprintf(stderr,"Entering repeating mode.\n");
sds[1]=create_socket(listen_port,1);
if(sds[1]<0){
fprintf(stderr,"Cannot bind to port %d.\n",listen_port);
exit(-1);
}
while(1){
if(wait_activity(sds,ready,2)<0){
fprintf(stderr,"No more activity on port %d, exiting.\n",listen_port);
exit(0);
}
if(ready[0]==1) repeat_packet(sds[0],sds[1],listen_port);
if(ready[1]==1) repeat_packet(sds[1],sds[1],repeat_port);
}
}
This document was translated from LATEX by
HEVEA.