Client IPv4/IPv6 : comment coder proprement ?

Le
Olivier Miakinen
[diapublication, suivi vers fr.comp.reseaux.ip]

Bonjour,

Grâce à Jérémie Courrèges-Anglas sur fr.comp.reseaux.ip, j'ai déjà eu
pas mal de réponses très intéressantes concernant la programmation des
sockets en IPv4 et IPv6.

Pour cette nouvelle question, je fais de nouveau une diapublication dans
trois groupes (f.c.reseaux.ip, f.c.os.unix et f.c.lang.c) avec suivi
vers fr.comp.reseaux.ip seul.

***

À l'époque d'IPv4 il était exceptionnel qu'une machine non routeur
ait plus d'une adresse IP non locale, mais avec IPv6 au contraire
c'est la règle d'avoir plusieurs adresses IP.

Comme en plus les adresses sont longues comme un jour sans pain et
immémorisables, il est conseillé de ne retenir que le nom DNS d'une
machine à laquelle on veut se connecter par TCP, et d'appliquer le
petit algorithme suivant :
- faire un getaddrinfo(hostname, port) pour obtenir toutes les
adresses correspondantes ;
- faire une boucle sur chaque adresse pour tenter de faire le
socket() puis le connect() jusqu'à ce que la connexion réussisse.
Cf. à la fin de cet article une fonction do_connect() écrite en C
et implémentant cet algorithme (j'espère ne pas avoir laissé traîné
d'erreur).

***

Tout cela est bel et bon s'agissant d'une socket connectée (SOCK_STREAM,
typiquement TCP). Mais ce que je voudrais savoir, c'est comment diable
on peut bien faire dans le cas d'une socket en SOCK_DGRAM (UDP) ! Non
seulement il n'y a pas de connect() pour nous dire si l'adresse distante
est la bonne, mais même le premier sendto() pourrait très bien ne pas
retourner d'erreur alors que l'hôte distant ne recevra jamais le paquet.

Et donc comment faire ? J'ai essayé de trouver des réponses sur le web,
mais je ne vois que des exemples en SOCK_STREAM.

***

P.-S. : le code pour socket connectée :

int do_connect(const char *hostname, const char *port)
{
struct addrinfo hints;
struct addrinfo *result, *aip;
int cc;
int sock;

memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

cc = getaddrinfo(hostname, port, &hints, &result);
if (cc != 0) {
fprintf(stderr, "getaddrinfo: %s", gai_strerror(cc));
return -1;
}

for (aip = result; aip != NULL; aip = aip->ai_next) {
sock = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
if (sock < 0)
continue;

if (connect(sock, aip->ai_addr, aip->ai_addrlen) == 0)
break; /* success */

close(sock);
}

freeaddrinfo(result);

if (aip == NULL) { /* No address succeeded */
fprintf(stderr, "Could not connect");
return -1;
}

return sock;
}



Cordialement,
--
Olivier Miakinen
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
espie
Le #25192792
In article Olivier Miakinen
[diapublication, suivi vers fr.comp.reseaux.ip]

Bonjour,

Grâce à Jérémie Courrèges-Anglas sur fr.comp.reseaux.ip, j'ai déjà eu
pas mal de réponses très intéressantes concernant la programmation des
sockets en IPv4 et IPv6.

Pour cette nouvelle question, je fais de nouveau une diapublication dans
trois groupes (f.c.reseaux.ip, f.c.os.unix et f.c.lang.c) avec suivi
vers fr.comp.reseaux.ip seul.

***

À l'époque d'IPv4 il était exceptionnel qu'une machine non routeur
ait plus d'une adresse IP non locale, mais avec IPv6 au contraire
c'est la règle d'avoir plusieurs adresses IP.

Comme en plus les adresses sont longues comme un jour sans pain et
immémorisables, il est conseillé de ne retenir que le nom DNS d'une
machine à laquelle on veut se connecter par TCP, et d'appliquer le
petit algorithme suivant :
- faire un getaddrinfo(hostname, port) pour obtenir toutes les
adresses correspondantes ;
- faire une boucle sur chaque adresse pour tenter de faire le
socket() puis le connect() jusqu'à ce que la connexion réussisse.
Cf. à la fin de cet article une fonction do_connect() écrite en C
et implémentant cet algorithme (j'espère ne pas avoir laissé traîné
d'erreur).

***

Tout cela est bel et bon s'agissant d'une socket connectée (SOCK_STREAM,
typiquement TCP). Mais ce que je voudrais savoir, c'est comment diable
on peut bien faire dans le cas d'une socket en SOCK_DGRAM (UDP) ! Non
seulement il n'y a pas de connect() pour nous dire si l'adresse distante
est la bonne, mais même le premier sendto() pourrait très bien ne pas
retourner d'erreur alors que l'hôte distant ne recevra jamais le paquet.

Et donc comment faire ? J'ai essayé de trouver des réponses sur le web,
mais je ne vois que des exemples en SOCK_STREAM.



Plusieurs possibilites, dont esperer que tu as affaire a un protocole qui
te donne un retour, et decider apres un moment que c'est mort, d'essayer
le suivant. Ou essayer la requete sur plusieurs adresses, et garder celle
qui repond la premiere...

je crains que tu sois bon pour faire du select/poll pour ca.

La raison pour laquelle il y a peu d'exemples en UDP, c'est que comme le
systeme ne gere presque plus rien pour toi, c'est difficile de donner des
exemples "generiques" qui n'ont pas de composante "application" dans ce qu'il
faut faire. En general, c'est un peu tout un ensemble, tu fais de l'UDP pour
aller plus vite, et donc il faut un peu tout regarder...
Xavier Roche
Le #25192942
Le 02/02/2013 10:47, Marc Espie a écrit :
Plusieurs possibilites, dont esperer que tu as affaire a un protocole qui
te donne un retour, et decider apres un moment que c'est mort, d'essayer
le suivant. Ou essayer la requete sur plusieurs adresses, et garder celle
qui repond la premiere...



De toute manière, UDP, à la base, vous êtes soumis aux aléas:
- paquets perdus
- ordonnancement aléatoire
- route qui est coupée subitement

Du coup j'imagine qu'il faut gérer des timeouts, des retransmissions,
des accusés de réception - bref tout le bazar que gère TCP.

Après, ça peut être une idée de gérer ça encore mieux que TCP - pourquoi
pas tourner sur les IP disponibles et calculer le temps de ping par
exemple, ainsi que le taux de paquets perdus ou désordonnés, et au final
avoir un algorithme qui sélectionne la meilleure destination en fonction
de la note de l'hôte (qui peut varier avec le temps).

(Vaste sujet)
Olivier Miakinen
Le #25197932
Bonjour,

Le 02/02/2013 10:47, Marc Espie m'a répondu :

Tout cela est bel et bon s'agissant d'une socket connectée (SOCK_STREAM,
typiquement TCP). Mais ce que je voudrais savoir, c'est comment diable
on peut bien faire dans le cas d'une socket en SOCK_DGRAM (UDP) ! Non
seulement il n'y a pas de connect() pour nous dire si l'adresse distante
est la bonne, mais même le premier sendto() pourrait très bien ne pas
retourner d'erreur alors que l'hôte distant ne recevra jamais le paquet.

Et donc comment faire ? J'ai essayé de trouver des réponses sur le web,
mais je ne vois que des exemples en SOCK_STREAM.



Plusieurs possibilites, dont esperer que tu as affaire a un protocole qui
te donne un retour, et decider apres un moment que c'est mort, d'essayer
le suivant.



Le protocole qui m'intéresse est SNMP (Simple Network Management
Protocol) [1]. Lorsque le superviseur (manager en anglais) fait des
requêtes vers les agents, le protocole donne un retour. Inversement,
quand ce sont les agents qui envoient des infos au superviseur, sauf
cas particulier qui n'existait pas en version 1 (et qu'on ne veut
pas forcément utiliser en version 3) il n'y a pas de retour.

Ou essayer la requete sur plusieurs adresses, et garder celle
qui repond la premiere...



Certes. Mais quand un superviseur gère déjà des milliers d'agents,
ça fait un gros paquet de requêtes au démarrage s'il faut les
multiplier par le nombre d'adresses possibles.

Et bien sûr on ne peut l'envisager que pour des requêtes de type
interrogation (GET), pas pour celles qui ont potentiellement une
action du style « imprime moi le document de 500 pages ».

je crains que tu sois bon pour faire du select/poll pour ca.



C'était déjà le cas, du fait qu'il y avait une socket en SOCK_RAW
pour recevoir les paquets ICMP (dont 'destination unreachable').

La raison pour laquelle il y a peu d'exemples en UDP, c'est que comme le
systeme ne gere presque plus rien pour toi, c'est difficile de donner des
exemples "generiques" qui n'ont pas de composante "application" dans ce qu'il
faut faire. En general, c'est un peu tout un ensemble, tu fais de l'UDP pour
aller plus vite, et donc il faut un peu tout regarder...



Historiquement, SNMP est sur UDP car l'idée était en premier lieu de
surveiller le réseau lui-même, or sur un réseau qui fonctionne mal
on a plus de chances d'obtenir une réponse en UDP (deux paquets
suffisent : requête + réponse) qu'en TCP (où il faut au moins cinq
paquets en comptant SYN, SYN+ACK et ACK).

Depuis SNMP sert tout autant pour gérer des applications sur un réseau
en bonne santé (imprimantes par exemple), et là TCP aurait été tout
aussi bien, mais malheureusement ce n'est pas prévu.


Merci pour tes réponses en tout cas. Je dois avouer que tu ne
me donnes pas de solution à laquelle je n'avais pas déjà pensé
moi-même, mais cela semble confirmer qu'il n'y a pas d'autre
méthode, et ça c'est déjà beaucoup.


[1] http://en.wikipedia.org/wiki/Simple_Network_Management_Protocol
Simple Network Management Protocol (la page équivalente en français
ne donne les références RFC que pour la version 1)

--
Olivier Miakinen
Olivier Miakinen
Le #25197972
Bonjour,

[Je réponds dans le cas qui m'intéresse, à savoir SNMP]

Le 02/02/2013 12:05, Xavier Roche répondait à Marc Espie :

Plusieurs possibilites, dont esperer que tu as affaire a un protocole qui
te donne un retour, et decider apres un moment que c'est mort, d'essayer
le suivant. Ou essayer la requete sur plusieurs adresses, et garder celle
qui repond la premiere...



De toute manière, UDP, à la base, vous êtes soumis aux aléas:
- paquets perdus
- ordonnancement aléatoire
- route qui est coupée subitement



Oui. Le cas des paquets perdus et des routes coupées est déjà prévu,
sachant que ça peut être aussi bien la requête que la réponse qui se
perd. En ce qui concerne l'ordonnancement, ce n'est pas un problème
en général car les requêtes et les réponses sont souvent courtes
(un seul paquet à la fois).

Du coup j'imagine qu'il faut gérer des timeouts, des retransmissions,
des accusés de réception - bref tout le bazar que gère TCP.



Exact.

Après, ça peut être une idée de gérer ça encore mieux que TCP - pourquoi
pas tourner sur les IP disponibles et calculer le temps de ping par
exemple, ainsi que le taux de paquets perdus ou désordonnés, et au final
avoir un algorithme qui sélectionne la meilleure destination en fonction
de la note de l'hôte (qui peut varier avec le temps).

(Vaste sujet)



Vaste sujet, oui. Merci de ta réponse également !

Cordialement,
--
Olivier Miakinen
Publicité
Poster une réponse
Anonyme