Le protocole
La question a surgi dans le groupe de
travail Behave de l'IETF, à propos du protocole
Les
Mais une application typique ne fabrique pas les paquets IP à la
main, bit par bit. Elle compte sur une API standard, typiquement sur
C'est pour répondre à cette question que j'ai développé un ensemble
de petits programmes, qu'on peut récupérer dans le fichier
Pour les compiler et les exécuter, voir les instructions dans le
fichier
% ./ip-header-set
Testing of the abilities to set bits in the IP header
My UID is 1000
The system is Linux 2.6.18-5-686, the machine is i686
TTL successfully set to 10
TOS successfully set to 8
ECN successfully set to 2
[PROBLEM] Cannot set the precedence to 160: (Operation not permitted)
DF set through PMTU successfully set to 2
DF clear through PMTU successfully set to 0
Warning: we only tested that setsockopt was happy,
not that the actual packet headers were changed
On voit que, sur
Testing of the abilities to set bits in the IP header
My UID is 54131
The system is FreeBSD 6.2-RELEASE, the machine is i386
TTL successfully set to 10
TOS successfully set to 8
ECN successfully set to 2
precedence successfully set to 160
No known way to set the DF bit on this system
Warning: we only tested that setsockopt was happy,
not that the actual packet headers were changed
Donc, la fonction de définition des options a marché. Mais le
paquet IP émis a t-il réellement changé ? Pour le savoir, nous
utilisons désormais deux programmes,
% ./sender test.example.org 10000
Testing of the abilities to set bits in the IP header
My UID is 1000
The system is Linux 2.6.18-5-686, the machine is i686
TTL successfully set to 10
TOS successfully set to 8
ECN successfully set to 2
[PROBLEM] Cannot set the precedence to 160:
DF set through PMTU successfully set to 2
DF clear through PMTU successfully set to 0
et, sur la machine de réception (ici, identique) :
% ./receiver 10000
Testing of the abilities to read bits in the IP header
My UID is 1000
The system is Linux 2.6.18-5-686, the machine is i686
Received 5 bytes
The TTL is 10
The TOS/DSCP is 0
Received 5 bytes
The TTL is 64
The TOS/DSCP is 8
Received 5 bytes
The TTL is 64
The TOS/DSCP is 2
Received 5 bytes
The TTL is 64
The TOS/DSCP is 0
Received 5 bytes
The TTL is 64
The TOS/DSCP is 0
Received 5 bytes
The TTL is 64
The TOS/DSCP is 0
On voit que l'API disponible sur Linux permet de lire les options, et
qu'elles ont la bonne valeur. Si on n'a pas confiance, on peut
vérifier avec un
09:44:00.127838 IP (tos 0x0, ttl 10, id 35166, offset 0, flags [DF], proto: UDP (17), length: 33) 192.0.2.69.47491 > 192.0.2.69.10000: UDP, length 5
09:44:00.127873 IP (tos 0x8, ttl 64, id 35166, offset 0, flags [DF], proto: UDP (17), length: 33) 192.0.2.69.47491 > 192.0.2.69.10000: UDP, length 5
Sur cette trace des deux premiers paquets, on voit le changement du
TTL à 10, et on voit le TOS mis à 8.
Comment travaillent ces programmes ? Pour définir les options comme
le TOS/DSCP ou comme ECN, ils utilisent l'appel système
myecn = 0x02; /* ECN-capable transport */
result = setsockopt(mysocket, IPPROTO_IP,
IP_TOS, myecn, sizeof(int));
On note que Posix appelle toujours TOS (cf. la constante
Pour fixer le TTL, le principe est le même. Mais pour mettre le bit
DF (
result = setsockopt(mysocket, IPPROTO_IP,
IP_MTU_DISCOVER, IP_PMTUDISC_DO, sizeof(int));
Et pour lire les options ? C'est nettement plus compliqué. Une
méthode assez peu standard existe, qui dépend des messages de contrôle
des prises (cf.
msg_iov = malloc(sizeof(struct iovec));
/* Les données seront mises dans "buffer" */
message->msg_iov->iov_base = buffer;
message->msg_iov->iov_len = length;
message->msg_iovlen = 1;
/* Et les messages de contrôle dans "control_buffer" */
message->msg_control = control_buffer;
message->msg_controllen = control_length;
result = recvmsg(mysocket, message, 0);
if (result <= 0) {
err_ret("[PROBLEM] Cannot receive data (%s)", sys_err_str());
} else {
fprintf(stdout, "Received %i bytes\n", result);
for (c_msg = CMSG_FIRSTHDR(message); c_msg;
c_msg = (CMSG_NXTHDR(message, c_msg))) {
if (c_msg->cmsg_level == IPPROTO_IP &&
c_msg->cmsg_type == IP_TTL) {
fprintf(stdout, "\tThe TTL is %i\n",
*(int *) CMSG_DATA(c_msg));
]]>
Merci au regretté