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.