Je suis Charlie

Autres trucs

Accueil

Seulement les RFC

Seulement les fiches de lecture

Ève

Ce blog n'a d'autre prétention que de me permettre de mettre à la disposition de tous des petits textes que j'écris. On y parle surtout d'informatique mais d'autres sujets apparaissent parfois.


RFC 8335: PROBE: A Utility For Probing Interfaces

Date de publication du RFC : Février 2018
Auteur(s) du RFC : R. Bonica (Juniper), R. Thomas (Juniper), J. Linkova (Google), C. Lenart (Verizon), M. Boucadair (Orange)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF intarea
Première rédaction de cet article le 23 février 2018


Pour tester qu'une machine est bien joignable, vous utilisez ping ou, plus rigoureusement, vous envoyez un message ICMP de type echo, auquel la machine visée va répondre avec un message ICMP echo reply. Ce test convient souvent mais il a plusieurs limites. L'une des limites de ce test est qu'il ne teste qu'une seule interface réseau de la machine, celle par laquelle vous lui parlez (deux interfaces, dans certains cas de routage asymétrique). Si la machine visée est un gros routeur avec plein d'interfaces réseau, le test ne vous dira pas si toutes fonctionnent. D'où cette extension aux messages ICMP permettant de spécifier l'interface qu'on veut vérifier.

A priori, ce RFC ne s'intéresse qu'aux routeurs, les serveurs n'ayant souvent qu'une seule interface réseau. La nouvelle technique, nommée PROBE, n'a pas de vocation générale, contrairement à ping, et concernera surtout les administrateurs réseau. D'autant plus que, comme elle est assez indiscrète, elle ne sera a priori pas ouverte au public. Notez qu'elle permet non seulement de tester une autre interface du routeur, mais également une interface d'une machine directement connectée au routeur. Les scénarios d'usage proposés sont exposés dans la section 5, une liste non limitative de cas où ping ne suffit pas :

  • Interface réseau non numérotée (pas d'adresse, ce qui est relativement courant sur les routeurs),
  • Interface réseau numérotée de manière purement locale (par exemple adresse IPv6 link-local),
  • Absence de route vers l'interface testée (si on veut tester l'interface d'un routeur qui fait face à un point d'échange, et que le préfixe du point d'échange n'est pas annoncé par le protocole de routage, ce qui est fréquent).

En théorie, SNMP pourrait servir au moins partiellement à ces tests mais, en pratique, c'est compliqué.

ping, la technique classique, est très sommairement décrit dans le RFC 2151, section 3.2, mais sans indiquer comment il fonctionne. La méthologie est simple : la machine de test envoie un message ICMP Echo (type 8 en IPv4 et 128 en IPv6) à la machine visée (l'amer). L'amer répond avec un Echo Reply (type 0 en IPv4 et 129 en IPv6). La réception de cet Echo Reply indique que la liaison marche bien dans les deux sens. La non-réception indique d'un problème s'est produit, mais on n'en sait pas plus (notamment, on ne sait pas si le problème était à l'aller ou bien au retour). Ici, on voit le test effectué par une sonde Atlas sur l'amer 2605:4500:2:245b::42 (l'un des serveurs hébergeant ce blog), vu par tshark :

13.013422 2a02:1811:c13:1902:1ad6:c7ff:fe2a:6ac → 2605:4500:2:245b::42 ICMPv6 126 Echo (ping) request id=0x0545, seq=1, hop limit=56
13.013500 2605:4500:2:245b::42 → 2a02:1811:c13:1902:1ad6:c7ff:fe2a:6ac ICMPv6 126 Echo (ping) reply id=0x0545, seq=1, hop limit=64 (request in 11)
    

ICMP est normalisé dans les RFC 792 pour IPv4 et RFC 4443 pour IPv6. L'exemple ci-dessus montre un test classique, avec une requête et une réponse.

Notre RFC parle d'« interface testée » (probed interface) et d'« interface testante » (probing interface). Dans l'exemple ci-dessus, l'interface Ethernet de l'Atlas était la testante et celle du serveur était la testée. Le succès du test montre que les deux interfaces sont actives et peuvent se parler.

Au contraire de ping, PROBE va envoyer le message, non pas à l'interface testée mais à une interface « relais » (proxy). Celle-ci répondra si l'interface testée fonctionne bien (état oper-status, cf. RFC 7223). Si l'interface testée n'est pas sur le nœud qui sert de relais, ce dernier détermine l'état de cette interface en regardant la table ARP (RFC 826) ou NDP (RFC 4861). Aucun test actif n'est effectué, l'interface est considérée comme active si on lui a parlé récemment (et donc si l'adresse IP est dans un cache). PROBE utilise, comme ping, ICMP. Il se sert des messages ICMP structurés du RFC 4884. Une des parties du message structuré sert à identifier l'interface testée.

L'extension à ICMP Extended Echo est décrite en section 2 du RFC. Le type de la requête est 42 pour IPv4 et 160 pour IPv6 (enregistré à l'IANA, pour IPv4 et IPv6). Parmi les champs qu'elle comprend (les deux premiers existent aussi pour l'ICMP Echo traditionnel) :

  • Un identificateur qui peut servir à faire correspondre une réponse à une requête (ICMP, comme IP, n'a pas de connexion ou de session, chaque paquet est indépendant), 0x0545 dans l'exemple vu plus haut avec tshark,
  • Un numéro de séquence, qui peut indiquer les paquets successifs d'un même test, 1 dans l'exemple vu plus haut avec tshark
  • Un bit nommé L (local) qui indique si l'interface testée est sur le nœud visé par le test ou non,
  • Une structure qui indique l'interface testée.

Cette structure suit la forme décrite dans la section 7 du RFC 4884. Elle contient un objet d'identification de l'interface. L'interface qu'on teste peut être désignée par son adresse IP (si elle n'est pas locale - bit L à zéro, c'est la seule méthode acceptée), son nom ou son index. Notez que l'adresse IP identifiant l'adresse testée n'est pas forcément de la même famille que celle du message ICMP. On peut envoyer en IPv4 un message ICMP demandant à la machine distante de tester une interface IPv6.

Plus précisément, l'objet d'identification de l'interface est composé, comme tous les objets du RFC 4884, d'un en-tête et d'une charge utile. L'en-tête contient les champs :

  • Un numéro de classe, 3, stocké à l'IANA,
  • Un numéro indiquant comment l'interface testée est désignée (1 = par nom, cf. RFC 7223, 2 = par index, le même qu'en SNMP, voir aussi le RFC 7223, section 3, le concept de if-index, et enfin 3 = par adresse),
  • La longueur des données.

L'adresse est représentée elle-même par une structure à trois champs, la famille (4 pour IPv4 et 6 pour IPv6), la longueur et la valeur de l'adresse. Notez que le RFC 5837 a un mécanisme de description de l'interface, portant le numéro de classe 2, et utilisé dans un contexte proche.

La réponse à ces requêtes a le type 43 en IPv4 et 161 en IPv6 (section 3 du RFC). Elle comprend :

  • Un code (il valait toujours 0 pour la requête) qui indique le résultat du test : 0 est un succès, 1 signale que la requête était malformée, 2 que l'interface à tester n'existe pas, 3 qu'il n'y a pas de telle entrée dans la table des interfaces, et 4 que plusieurs interfaces correspondent à la demande (liste complète dans un registre IANA),
  • Un identificateur, copié depuis la requête,
  • Un numéro de séquence, copié depuis la requête,
  • Un état qui donne des détails en cas de code 0 (autrement, pas besoin de détails) : si l'interface testée n'est pas locale, l'état vaut 2 si l'entrée dans le cache ARP ou NDP est active, 1 si elle est incomplète (ce qui indique typiquement que l'interface testée n'a pas répondu), 3 si elle n'est plus à jour, etc,
  • Un bit A (active) qui est mis à un si l'interface testée est locale et fonctionne,
  • Un bit nommé 4 (pour IPv4) qui indique si IPv4 tourne sur l'interface testée,
  • Un bit nommé 6 (pour IPv6) qui indique si IPv6 tourne sur l'interface testée.

La section 4 du RFC détaille le traitement que doit faire la machine qui reçoit l'ICMP Extended Echo. D'abord, elle doit jeter le paquet (sans répondre) si ICMP Extended Echo n'est pas explicitement activé (rappelez-vous que ce service est assez indiscret, cf. section 8 du RFC) ou bien si l'adresse IP de la machine testante n'est pas autorisée (même remarque). Si les tests sont passés et que la requête est acceptée, la machine réceptrice fabrique une réponse : le code est mis à 1 si la requête est anormale (pas de partie structurée par exemple), 2 si l'interface testée n'existe pas, 3 si elle n'est pas locale et n'apparait pas dans les tables (caches) ARP ou NDP. Si on trouve l'interface, on la teste et on remplit les bits A, 4, 6 et l'état, en fonction de ce qu'on trouve sur l'interface testée.

Reste la question de la sécurité (section 8 du RFC). Comme beaucoup de mécanismes, PROBE peut être utilisé pour le bien (l'administrateur réseaux qui détermine l'état d'une interface d'un routeur dont il s'occupe), mais aussi pour le mal (chercher à récolter des informations sur un réseau avant une attaque, par exemple, d'autant plus que les noms d'interfaces dans les routeurs peuvent être assez parlants, révélant le type de réseau, le modèle de routeur…) Le RFC exige donc que le mécanisme ICMP Extended Echo ne soit pas activé par défaut, et soit configurable (liste blanche d'adresses IP autorisées, permission - ou non - de tester des interfaces non locales, protection des différents réseaux les uns contre les autres, si on y accueille des clients différents…) Et, bien sûr, il faut pouvoir limiter le nombre de messages.

Ne comptez pas utilise PROBE tout de suite. Il n'existe apparemment pas de mise en œuvre de ce mécanisme publiée. Juniper en a réalisé une mais elle n'apparait encore dans aucune version de JunOS.


Téléchargez le RFC 8335


L'article seul

RFC 8315: Cancel-Locks in Netnews articles

Date de publication du RFC : Février 2018
Auteur(s) du RFC : M. Bäuerle (STZ Elektronik)
Chemin des normes
Première rédaction de cet article le 14 février 2018


Cela peut sembler étonnant, mais le service des News fonctionne toujours. Et il est régulièrement perfectionné. Ce nouveau RFC normalise une extension au format des articles qui permettra de sécuriser un petit peu l'opération d'annulation d'articles.

Une fois qu'un article est lancé sur un réseau social décentralisé, comme Usenet (RFC 5537), que faire si on le regrette ? Dans un système centralisé comme Twitter, c'est simple, on s'authentifie, on le supprime et plus personne ne le voit. Mais dans un réseau décentralisé, il faut encore propager la demande de suppression (d'annulation, sur les News). Et cela pose évidemment des questions de sécurité : il ne faut pas permettre aux méchants de fabriquer trop facilement une demande d'annulation. Notre RFC propose donc une mesure de sécurité, l'en-tête Cancel-Lock:.

Cette mesure de sécurité est simple, et ne fournit certainement pas une sécurité de niveau militaire. Pour la comprendre, il faut revenir au mécanisme d'annulation d'un article d'Usenet. Un article de contrôle est un article comme les autres, envoyé sur Usenet, mais il ne vise pas à être lu par les humains, mais à être interprété par le logiciel. Un exemple d'article de contrôle est l'article de contrôle d'annulation, défini dans le RFC 5337, section 5.3. Comme son nom l'indique, il demande la suppression d'un article, identifié par son Message ID. Au début d'Usenet, ces messages de contrôle n'avaient aucune forme d'authentification. On a donc vu apparaitre des faux messages de contrôle, par exemple à des fins de censure (supprimer un article qu'on n'aimait pas). Notre nouveau RFC propose donc qu'un logiciel proche de la source du message mette un en-tête Cancel-Lock: qui indique la clé qui aura le droit d'annuler le message.

Évidemment, ce Cancel-Lock: n'apporte pas beaucoup de sécurité, entre autre parce qu'un serveur peut toujours le retirer et mettre le sien, avant de redistribuer (c'est évidemment explicitement interdit par le RFC mais il y a des méchants). Mais cela ne change de toute façon pas grand'chose à la situation actuelle, un serveur peut toujours jeter un article, de toute façon. Si on veut quand même une solution de sécurité « sérieuse », il faut utiliser PGP, comme mentionné en passant par le RFC 5537 (mais jamais normalisé dans un RFC).

La section 2 du RFC décrit en détail le mécanisme de sécurité. La valeur de l'en-tête Cancel-Lock: est l'encodage en base64 d'une condensation d'une clé secrète (en fait, on devrait plutôt l'appeler mot de passe). Pour authentifier une annulation, le message de contrôle comportera un autre en-tête, Cancel-Key:, qui révélera la clé (qui ne devra donc être utilisée qu'une fois).

Voici un exemple. On indique explicitement l'algorithme de condensation (ici, SHA-256, la liste est dans un registre IANA). D'abord, le message original aura :

Cancel-Lock: sha256:s/pmK/3grrz++29ce2/mQydzJuc7iqHn1nqcJiQTPMc=
    

Et voici le message de contrôle, authentifié :

Cancel-Key: sha256:qv1VXHYiCGjkX/N1nhfYKcAeUn8bCVhrWhoKuBSnpMA=
    

La section 3 du RFC détaille comment on utilise ces en-têtes. Le Cancel-Lock: peut être mis par l'auteur originel de l'article, ou bien par un intermédiaire (par exemple le modérateur qui a approuvé l'article). Plusieurs Cancel-Lock: peuvent donc être présents. Notez qu'il n'y a aucun moyen de savoir si le Cancel-Lock: est « authentique ». Ce mécanisme est une solution de sécurité faible.

Pour annuler un message, on envoie un message de contrôle avec un Cancel-Key: correspondant à un des Cancel-Lock:. Les serveurs recevant ce message de contrôle condenseront la clé (le mot de passe) et vérifieront s'ils retombent bien sur le condensat contenu dans un des Cancel-Lock:.

La section 4 donne les détails sur le choix de la clé (du mot de passe). Évidemment, elle doit être difficile à deviner, donc plutôt choisie par un algorithme pseudo-aléatoire (et pas "azerty123"). Et elle doit être à usage unique puisque, une fois révélée par un Cancel-Key:, elle n'est plus secrète. L'algorithme recommandé par le RFC est d'utiliser un HMAC (RFC 2104) d'un secret et de la concaténation du Message ID du message avec le nom de l'utilisateur. Comme cela, générer un Cancel-Key: pour un message donné peut se faire avec juste le message, sans avoir besoin de mémoriser les clés. Voici un exemple, tiré de la section 5, et utilisant OpenSSL. Le secret est frobnicateZ32. Le message est le <12345@example.net> et l'utilisateur est stephane :

      
% printf "%s" "<12345@example.net>stephane" \
        | openssl dgst -sha256 -hmac "frobnicateZ32" -binary \
        | openssl enc -base64
f0rHwfZXp5iKFjTbX/I5bQXh9Dta33nWBzLi8f9oaoM=

    

Voilà, nous avons notre clé, f0rHwfZXp5iKFjTbX/I5bQXh9Dta33nWBzLi8f9oaoM=. Pour le condensat, nous nous servirons de SHA-256 :


% printf "%s" "f0rHwfZXp5iKFjTbX/I5bQXh9Dta33nWBzLi8f9oaoM=" \
        | openssl dgst -sha256 -binary \
        | openssl enc -base64
RBJ8ZsgqBnW/tYT/qu1JcXK8SA2O9g+qJLDzRY5h1cg=	
   
    

Nous pouvons alors former le Cancel-Lock: :

Cancel-Lock: sha256:RBJ8ZsgqBnW/tYT/qu1JcXK8SA2O9g+qJLDzRY5h1cg=
    

Et, si nous voulons annuler ce message, le Cancel-Key: dans le message de contrôle d'annulation aura l'air :

Control: cancel <12345@example.net>
Cancel-Key: sha256:f0rHwfZXp5iKFjTbX/I5bQXh9Dta33nWBzLi8f9oaoM=
    

Pour vérifier ce message de contrôle, le logiciel calculera le condensat de la clé et vérifiera s'il retombe bien sur RBJ8ZsgqBnW/tYT/qu1JcXK8SA2O9g+qJLDzRY5h1cg=.

Enfin, la section 7 du RFC détaille la sécurité de ce mécanisme. Cette sécurité est plutôt faible :

  • Aucune protection de l'intégrité. Un intermédiaire a pu modifier, ajouter ou supprimer le Control-Lock:. Si cela vous défrise, vous devez utiliser PGP.
  • Lors d'une annulation, la clé est visible par tous, donc elle peut être copiée et utilisée dans un message de contrôle de remplacement (au lieu de l'annulation). Mais, de toute façon, un attaquant peut aussi bien faire un nouveau message faux (Usenet ne vérifie pas grand'chose).
  • Avant ce RFC, ce mécanisme était déjà largement utilisé, depuis longtemps, et souvent en utilisant SHA-1 comme fonction de condensation. Contrairement à ce qu'on lit parfois, SHA-1 n'est pas complètement cassé : il n'y a pas encore eu de publication d'une attaque pré-image pour SHA-1 (seulement des collisions). Néanmoins, SHA-1 ne peut plus être considéré comme sûr, d'autant plus que Usenet évolue très lentement : un logiciel fait aujourd'hui va rester en production longtemps et, avec le temps, d'autres attaques contre SHA-1 apparaitront. D'où la recommandation du RFC de n'utiliser que SHA-2.

Les deux nouveaux en-têtes ont été ajoutés au registre des en-têtes.

À noter que, comme il n'y a pas de groupe de travail IETF sur les News, ce RFC a été surtout discuté… sur les News, en l'occurrence les groupes news.software.nntp et de.comm.software.newsserver. Comme ce RFC décrit une technique ancienne, il y a déjà de nombreuses mises en œuvre comme la bibliothèque canlock (paquetage Debian libcanlock2), dans le serveur INN, ou les clients News Gnus (regardez cet article sur son usage), flnews, slrn ou tin. Vous pouvez aussi lire l'article de Brad Templeton comparant Cancel-Lock: aux signatures.

Merci à Julien Élie pour sa relecture.


Téléchargez le RFC 8315


L'article seul

Meltdown & Spectre

First publication of this article on 10 February 2018


I didn't write about the hardware security vulnerabilities Meltdown and Spectre but André Sintzoff did and he asked for a place to put this online so, I host it here.

You can read his good summary (in English) of Meltdown and Spectre in the following formats:


L'article seul

RFC 8312: CUBIC for Fast Long-Distance Networks

Date de publication du RFC : Février 2018
Auteur(s) du RFC : I. Rhee (NCSU), L. Xu (UNL), S. Ha (Colorado), A. Zimmermann, L. Eggert (NetApp), R. Scheffenegger
Pour information
Réalisé dans le cadre du groupe de travail IETF tcpm
Première rédaction de cet article le 8 février 2018


Longtemps après sa mise au point et son déploiement massif sur Linux, voici la description officielle de l'algorithme CUBIC, un algorithme de contrôle de la congestion dans TCP.

CUBIC doit son nom au fait que la fonction de calcul de la fenêtre d'envoi des données est une fonction cubique (elle a un terme du troisième degré) et non pas linéaire. CUBIC est l'algorithme utilisé par défaut dans Linux depuis pas mal d'années :

% sudo sysctl net.ipv4.tcp_congestion_control
net.ipv4.tcp_congestion_control = cubic
    

CUBIC est plus énergique lorsqu'il s'agit d'agrandir la fenêtre d'envoi de données, lorsque le réseau a une grande capacité mais un RTT important. Dans le cas de ces réseaux « éléphants » (terme issu de la prononciation en anglais de LFN, Long and Fat Network, voir RFC 7323, section 1.1), le RTT élevé fait que l'émetteur TCP met du temps à recevoir les accusés de réception, et donc à comprendre que tout va bien et qu'il peut envoyer d'avantage de données, pour remplir le long tuyau. CUBIC permet d'accélérer cette phase.

Notez que CUBIC ne contrôle que l'émetteur, le récepteur est inchangé. Cela facilite le déploiement : un émetteur CUBIC peut parfaitement communiquer avec un récepteur traditionnel.

Avant de lire la suite du RFC, il est recommandé de (re)lire le RFC 5681, la bible sur le contrôle de congestion TCP, et notamment sur cette notion de fenêtre d'envoi (ou fenêtre de congestion).

TCP (RFC 793) a évidemment une mission difficile. L'intérêt de l'émetteur est d'envoyer le plus de données le plus vite possible. Mais à condition qu'elles arrivent sinon, s'il y a de la congestion, les données seront perdues et il faudra recommencer (ré-émettre). Et on n'est pas tout seul sur le réseau : il faut aussi tenir compte des autres, et chercher à partager équitablement l'Internet. L'algorithme doit donc être énergique (chercher à utiliser les ressources au maximum) mais pas bourrin (il ne faut pas dépasser le maximum), tout en étant juste (on n'est pas dans la startup nation, il ne faut pas écraser les autres, mais partager avec eux).

Le problème des éléphants, des réseaux à fort BDP, est connu depuis longtemps (article de T. Kelly, « Scalable TCP: Improving Performance in HighSpeed Wide Area Networks », et RFC 3649.) Dans ces réseaux, TCP tend à être trop prudent, à ouvrir sa fenêtre (les données qu'on peut envoyer tout de suite) trop lentement. Cette prudence l'honore, mais peut mener à des réseaux qui ne sont pas utilisés à fond. L'article de Ha, S., Kim, Y., Le, L., Rhee, I., et L. Xu, « A Step toward Realistic Performance Evaluation of High-Speed TCP Variants » expose ce problème. Il touche toutes les variantes de TCP depuis le TCP Reno décrit dans le RFC 5681 : le New Reno des RFC 6582 et RFC 6675, et même des protocoles non-TCP mais ayant le même algorithme, comme UDP (TFRC, RFC 5348) ou SCTP (RFC 4960).

CUBIC a été originellement documenté dans l'article de S. Ha, Injong Rhee, et Lisong Xu, « CUBIC: A New TCP-Friendly High-Speed TCP Variant », en 2008. Sur Linux, il a remplacé BIC pour les réseaux à haut BDP.

La section 3 du RFC rappelle les principes de conception de CUBIC, notamment :

  • Utilisation de la partie concave (la fenêtre s'agrandit rapidement au début puis plus lentement ensuite) et de la partie convexe de la fonction, et pas seulement la partie convexe (on ouvre la fenêtre calmement puis on accélère), comme l'ont tenté la plupart des propositions alternatives. Si vous avez du mal avec les termes concave et convexe, la figure 2 de cet article de comparaison de CUBIC et BIC illustre bien, graphiquement, ces concepts. La courbe est d'abord concave, puis convexe.
  • Comportement identique à celui de ses prédécesseurs pour les liaisons à faible RTT (ou faible BDP). Les algorithmes TCP traditionnels n'ont en effet pas de problème dans ce secteur (cf. section 4.2, et Floyd, S., Handley, M., et J. Padhye, « A Comparison of Equation-Based and AIMD Congestion Control »). « Si ce n'est pas cassé, ne le réparez pas. » CUBIC ne se différencie donc des autres algorithmes que pour les réseaux à RTT élevé, ce que rappelle le titre de notre RFC.
  • Juste partage de la capacité entre des flots ayant des RTT différents.
  • CUBIC mène à un agrandissement plus rapide de la fenêtre d'envoi, mais également à une réduction moins rapide lorsqu'il détecte de la congestion (paramètre « beta_cubic », le facteur de réduction de la fenêtre, voir aussi le RFC 3649.)

La section 4 du RFC spécifie précisement l'algorithme, après beaucoup de discussion avec les développeurs du noyau Linux (puisque le code a été écrit avant le RFC). Cette section est à lire si vous voulez comprendre tous les détails. Notez l'importance du point d'inflexion entre la partie concave et la partie convexe de la courbe qui décrit la fonction de changement de taille de la fenêtre. Ce point d'inflexion est mis à la valeur de la fenêtre d'envoi juste avant la dernière fois où TCP avait détecté de la congestion.

Notez que Linux met en outre en œuvre l'algorithme HyStart, décrit dans « Taming the Elephants: New TCP Slow Start ». Hystart mesure le RTT entre émetteur et récepteur pour détecter (par l'augmentation du RTT) un début de congestion avant que des pertes de paquets se produisent. (Merci à Lucas Nussbaum pour l'information.)

La section 5 analyse le comportement CUBIC selon les critères de comparaison des algorithmes de contrôle de la congestion décrits dans le RFC 5033.

Pour finir, voici une intéressante comparaison des algorithmes de contrôle de congestion.


Téléchargez le RFC 8312


L'article seul

RFC 8304: Transport Features of the User Datagram Protocol (UDP) and Lightweight UDP (UDP-Lite)

Date de publication du RFC : Février 2018
Auteur(s) du RFC : G. Fairhurst, T. Jones (University of Aberdeen)
Pour information
Réalisé dans le cadre du groupe de travail IETF taps
Première rédaction de cet article le 8 février 2018


Le RFC 8303, du groupe de travail TAPS, décrit les fonctions et services rendus par les protocoles de transport aux applications. Ce RFC 8304 se focalise sur deux protocoles sans connexion, UDP et UDP-Lite. Quels services fournissent-ils ?

UDP est normalisé dans le RFC 768 et UDP-Lite dans le RFC 3828. Tous les deux permettent l'envoi et la réception de datagrammes, sans connexion préalable, sans garantie d'être prévenu en cas de perte du datagramme, sans garantie qu'ils soient distribués dans l'ordre. Une application qui utilise UDP va donc devoir connaitre les services exacts que ce protocole fournit. Aujourd'hui, de nombreuses applications utilisent UDP, l'une des plus connues étant sans doute le DNS, sans compter celles qui utilisent un autre protocole de transport mais qui l'encapsulent dans UDP pour passer les boitiers intermédiaires (cf. par exemple RFC 8261). UDP-Lite est très proche d'UDP et ajoute juste la possibilité de distribuer à l'application des paquets potentiellement endommagés. Pour utiliser UDP intelligement, les programmeurs ont tout intérêt à consulter le RFC 8085.

L'API la plus courante est l'API dite « socket », normalisée par POSIX, et dont la meilleure documentation est évidemment le livre de Stevens. Les applications peuvent envoyer des données avec send(), sendto() et sendmsg(), en recevoir avec recvfrom() et recvmsg(). Elles peuvent être dans l'obligation de configurer certaines informations de bas niveau comme la permission de fragmenter ou pas, alors que TCP gère cela tout seul. Des options existent dans l'API socket pour définir ces options.

Notre RFC 8304 suit la démarche du RFC 8303 pour la description d'un protocole de transport. Plus précisement, il suite la première passe, celle de description de ce que sait faire le protocole.

Donc, pour résumer (section 3 du RFC), UDP est décrit dans le RFC 768, et UDP-Lite dans le RFC 3828. Tous les deux fournissent un service de datagramme, non connecté, et qui préserve les frontières de message (contrairement à TCP, qui envoie un flot d'octets). Le RFC 768 donne quelques idées sur l'API d'UDP, il faut notamment que l'application puisse écouter sur un port donné, et puisse choisir le port source d'envoi.

UDP marche évidemment également sur IPv6, et une extension à l'API socket pour IPv6 est décrite dans le RFC 3493.

UDP est très basique : aucun contrôle de congestion (le RFC 8085 détaille ce point), pas de retransmission des paquets perdus, pas de notification des pertes, etc. Tout doit être fait par l'application. On voit que, dans la grande majorité des cas, il vaut mieux utiliser TCP. Certains développeurs utilisent UDP parce qu'ils ont lu quelque part que c'était « plus rapide » mais c'est presque toujours une mauvaise idée.

Notre RFC décrit ensuite les primitives UDP utilisables par les applications :

  • CONNECT est très différent de celui de TCP. C'est une opération purement locale (aucun paquet n'est envoyé sur le réseau), qui associe des ports source et destination à une prise (socket), c'est tout. Une fois cette opération faite, on pourra envoyer ou recevoir des paquets. Comme CONNECT, CLOSE n'a d'effet que local (pas de connexion, donc pas de fermeture de connexion).
  • SEND et RECEIVE, dont le nom indique bien ce qu'elles font.
  • SET_IP_OPTIONS qui permet d'indiquer des options IP, chose qui n'est en général pas nécessaire en TCP mais peut être utile en UDP.
  • SET_DF est important car il permet de contrôler la fragmentation des paquets. TCP n'en a pas besoin car la MSS est négociée au début de la connexion. Avec UDP, c'est le programmeur de l'application qui doit choisir s'il laissera le système d'exploitation et les routeurs (en IPv4 seulement, pour les routeurs) fragmenter, ce qui est risqué, car pas mal de pare-feux mal configurés bloquent les fragments, ou bien s'il refusera la fragmentation (DF = Don't Fragment) et devra donc faire attention à ne pas envoyer des paquets trop gros. (Voir aussi les RFC 1191 et RFC 8201 pour une troisième possibilité.)
  • SET_TTL (IPv4) et SET_IPV6_UNICAST_HOPS permettent de définir le TTL des paquets IP envoyés. (Le terme de TTL est objectivement incorrect, et est devenu hops en IPv6, mais c'est le plus utilisé.)
  • SET_DSCP permet de définir la qualité de service souhaitée (RFC 2474).
  • SET_ECN permet de dire qu'on va utiliser ECN (RFC 3168). Comme les précédentes, elle n'a pas de sens en TCP, où le protocole de transport gère cela seul.
  • Enfin, dernière primitive citée par le RFC, la demande de notification des erreurs. Elle sert à réclamer qu'on soit prévenu de l'arrivée des messages ICMP.

Certaines primitives ont été exclues de la liste car n'étant plus d'actualité (cf. le RFC 6633).

Avec ça, on n'a parlé que d'UDP et pas d'UDP-Lite. Rappelons qu'UDP-Lite (RFC 3828) est à 95 % de l'UDP avec un seul changement, la possibilité que la somme de contrôle ne couvre qu'une partie du paquet. Il faut donc une primtive SET_CHECKSUM_COVERAGE qui indique jusqu'à quel octet le paquet est couvert par la somme de contrôle.

Notez enfin que le RFC se concentre sur l'unicast mais, si cela vous intéresse, l'annexe A décrit le cas du multicast.


Téléchargez le RFC 8304


L'article seul

RFC 8303: On the Usage of Transport Features Provided by IETF Transport Protocols

Date de publication du RFC : Février 2018
Auteur(s) du RFC : M. Welzl (University of Oslo), M. Tuexen (Muenster Univ. of Appl. Sciences), N. Khademi (University of Oslo)
Pour information
Réalisé dans le cadre du groupe de travail IETF taps
Première rédaction de cet article le 8 février 2018


La famille de protocoles TCP/IP dispose d'un grand nombre de protocoles de transport. On connait bien sûr TCP mais il y en a d'autres comme UDP et SCTP et d'autres encore moins connus. Cette diversité peut compliquer la tâche des développeurs d'application, qui ne savent pas toujours bien lequel choisir. Et elle pose également problème pour les développements futurs à l'IETF, par exemple la réflexion en cours sur les API. On a besoin d'une description claire des services que chaque protocole fournit effectivement. C'est le but du groupe de travail TAPS dont voici le deuxième RFC : une description de haut niveau des services que fournit la couche transport. À faire lire à tous les développeurs d'applications réseau, à tous les participants à la normalisation, et aux étudiants en réseaux informatiques.

Ce RFC parle de TCP, MPTCP, SCTP, UDP et UDP-Lite, sous l'angle « quels services rendent-ils aux applications ? » (QUIC, pas encore normalisé, n'y figure pas. LEDBAT, qui n'est pas vraiment un protocole de transport, est par contre présent.) L'idée est de lister un ensemble d'opérations abstraites, qui pourront ensuite être exportées dans une API. J'en profite pour recommander la lecture du RFC 8095, premier RFC du groupe TAPS.

La section 1 de notre RFC décrit la terminologie employée. Il faut notamment distinguer trois termes :

  • La fonction (Transport Feature) qui désigne une fonction particulière que le protocole de transport va effectuer, par exemple la confidentialité, la fiabilité de la distribution des données, le découpage des données en messages, etc.
  • Le service (Transport Service) qui est un ensemble cohérent de fonctions. C'est ce que demande l'application.
  • Le protocole (Transport Protocol) qui est une réalisation concrète d'un ou plusieurs services.

Un exemple de programme qui permet d'essayer différents types de prises (sockets) est socket-types.c. Vous pouvez le compiler et le lancer en indiquant le type de service souhaité. STREAM va donner du TCP, SEQPACKET du SCTP, DATAGRAM de l'UDP :

% make socket-types
cc -Wall -Wextra     socket-types.c   -o socket-types

% ./socket-types -v -p 853 9.9.9.9 STREAM
9.9.9.9 is an IP address
"Connection" STREAM successful to 9.9.9.9

% ./socket-types -v -p 443 -h dns.bortzmeyer.org STREAM 
Address(es) of dns.bortzmeyer.org is(are): 2605:4500:2:245b::42 204.62.14.153 
"Connection" STREAM successful to 2605:4500:2:245b::42
256 response bytes read
    

À première vue, l'API sockets nous permet de sélectionner le service, indépendamment du protocole qui le fournira. Mais ce n'est pas vraiment le cas. Si on veut juste un flot de données, cela pourrait être fait avec TCP ou SCTP, mais seul TCP sera sélectionné si on demande STREAM. En fait, les noms de services dns l'appel à socket() sont toujours en correspondance univoque avec les protocoles de transport.

Autres termes importants :

  • Adresse (Transport Address) qui est un tuple regroupant adresse IP, nom de protocole et numéro de port. Le projet TAPS considère uniquement des protocoles ayant la notion de ports, et fournissant donc la fonction de démultiplexage (différencier les données destinées à telle ou telle application).
  • Primitive (Primitive), un moyen pour l'application de demander quelque chose au protocole de transport.
  • Événement (Event), généré par le protocole de transport, et transmis à l'application pour l'informer.

La section 2 décrit le problème général. Actuellement, les applications sont liées à un protocole, même quand elles ne le souhaitent pas. Si une application veut que ses octets soient distribués dans l'ordre, et sans pertes, plusieurs protocoles peuvent convenir, et il n'est pas forcément utile de se limiter à seulement TCP, par exemple. Pour avancer vers un moindre couplage entre application et protocole, ce RFC décrit les fonctions et les services, un futur RFC définira des services minimaux, un autre peut-être une API.

Le RFC essaie de s'en tenir au minimum, des options utiles mais pas indispensables (comme le fait pour l'application de demander à être prévenue de certains changements réseau, cf. RFC 6458) ne sont pas étudiées.

Le RFC procède en trois passes : la première (section 3 du RFC) analyse et explique ce que chaque protocole permet, la seconde (section 4) liste les primitives et événements possibles, la troisième (section 5) donne pour chaque fonction les protocoles qui la mettent en œuvre. Par exemple, la passe 1 note que TCP fournit à l'application un moyen de fermer une connexion en cours, la passe 2 liste une primitive CLOSE.TCP, la passe 3 note que TCP et SCTP (mais pas UDP) fournissent cette opération de fermeture (qui fait partie des fonctions de base d'un protocole orienté connexion, comme TCP). Bon, d'accord, cette méthode est un peu longue et elle rend le RFC pas amusant à lire mais cela permet de s'assurer que rien n'a été oublié.

Donc, section 3 du RFC, la passe 1. Si vous connaissez bien tous ces protocoles, vous pouvez sauter cette section. Commençons par le protocole le plus connu, TCP (RFC 793). Tout le monde sait que c'est un protocole à connexion, fournissant un transport fiable et ordonné des données. Notez que la section 3.8 de la norme TCP décrit, sinon une API, du moins l'interface (les primitives) offertes aux applications. Vous verrez que les noms ne correspondent pas du tout à celle de l'API sockets (OPEN au lieu de connect/listen, SEND au lieu de write, etc).

TCP permet donc à l'application d'ouvrir une connexion (activement - TCP se connecte à un TCP distant - ou passivement - TCP attend les connexions), d'envoyer des données, d'en recevoir, de fermer la connexion (gentiment - TCP attend encore les données de l'autre - ou méchamment - on part en claquant la porte). Il y a aussi des événements, par exemple la fermeture d'une connexion déclenchée par le pair, ou bien une expiration d'un délai de garde. TCP permet également de spécifier la qualité de service attendue (cf. RFC 2474, et la section 4.2.4.2 du RFC 1123). Comme envoyer un paquet IP par caractère tapé serait peu efficace, TCP permet, pour les sessions interactives, genre SSH, d'attendre un peu pour voir si d'autres caractères n'arrivent pas (algorithme de Nagle, section 4.2.3.4 du RFC 1123). Et il y a encore d'autres possibilités moins connues, comme le User Timeout du RFC 5482. Je vous passe des détails, n'hésitez pas à lire le RFC pour approfondir, TCP a d'autres possibilités à offrir aux applications.

Après TCP, notre RFC se penche sur MPTCP. MPTCP crée plusieurs connexions TCP vers le pair, utilisant des adresses IP différentes, et répartit le trafic sur ces différents chemins (cf. RFC 6182). Le modèle qu'il présente aux applications est très proche de celui de TCP. MPTCP est décrit dans les RFC 6824 et son API est dans le RFC 6897. Ses primitives sont logiquement celles de TCP, plus quelques extensions. Parmi les variations par rapport à TCP, la primitive d'ouverture de connexion a évidemment une option pour activer ou pas MPTCP (sinon, on fait du TCP normal). Les extensions permettent de mieux contrôler quelles adresses IP on va utiliser pour la machine locale.

SCTP est le principal concurrent de TCP sur le créneau « protocole de transfert avec fiabilité de la distribution des données ». Il est normalisé dans le RFC 4960. Du point de vue de l'application, SCTP a trois différences importantes par rapport à TCP :

  • Au lieu d'un flot continu d'octets, SCTP présent une suite de messages, avec des délimitations entre eux,
  • Il a un mode où la distribution fiable n'est pas garantie,
  • Et il permet de tirer profit du multi-homing.

Il a aussi une terminologie différente, par exemple on parle d'associations et plus de connexions.

Certaines primitives sont donc différentes : par exemple, Associate au lieu du Open TCP. D'autres sont identiques, comme Send et Receive. SCTP fournit beaucoup de primitives, bien plus que TCP.

Et UDP ? Ce protocole sans connexion, et sans fiabilité, ainsi que sa variante UDP-Lite, est analysé dans un RFC séparé, le RFC 8304.

Nous passons ensuite à LEDBAT (RFC 6817). Ce n'est pas vraiment un protocole de transport, mais plutôt un système de contrôle de la congestion bâti sur l'idée « décroissante » de « on n'utilise le réseau que si personne d'autre ne s'en sert ». LEDBAT est pour les protocoles économes, qui veulent transférer des grandes quantités de données sans gêner personne. Un flot LEDBAT cédera donc toujours la place à un flot TCP ou compatible (RFC 5681). N'étant pas un protocole de transport, LEDBAT peut, en théorie, être utilisé sur les protocoles existants comme TCP ou SCTP (mais les extensions nécessaires n'ont hélas jamais été spécifiées). Pour l'instant, les applications n'ont pas vraiment de moyen propre d'activer ou de désactiver LEDBAT.

Bien, maintenant, passons à la deuxième passe (section 4 du RFC). Après la description des protocoles lors de la première passe, cette deuxième passe liste explicitement les primitives, rapidement décrites en première passe, et les met dans différentes catégories (alors que la première passe classait par protocole). Notons que certaines primitives, ou certaines options sont exclues. Ainsi, TCP a un mécanisme pour les données urgentes (RFC 793, section 3.7). Il était utilisé pour telnet (cf. RFC 854) mais n'a jamais vraiment fonctionné de manière satisfaisante et le RFC 6093 l'a officiellement exclu pour les nouvelles applications de TCP. Ce paramètre n'apparait donc pas dans notre RFC, puisque celui-ci vise le futur.

La première catégorie de primitives concerne la gestion de connexion. On y trouve d'abord les primitives d'établissement de connexion :

  • CONNECT.TCP : établit une connexion TCP,
  • CONNECT.SCTP : idem pour SCTP,
  • CONNECT.MPTCP : établit une connexion MPTCP,
  • CONNECT.UDP : établit une « connexion » UDP sauf, que, UDP étant sans connexion, il s'agit cette fois d'une opération purement locale, sans communication avec une autre machine.

Cette catégorie comprend aussi les primitives permettant de signaler qu'on est prêt à recevoir une connexion, LISTEN.TCP, celles de maintenance de la connexion, qui sont bien plus variées, et celles permettant de mettre fin à la connexion (comme CLOSE.TCP et CLOSE.SCTP, ou comme le signal qu'un délai de garde a été dépassé, TIMEOUT.TCP).

Dans les primitives de maintenance, on trouve aussi bien les changements du délai de garde (CHANGE_TIMEOUT.TCP et SCTP), le débrayage de l'algorithme de Nagle, pour TCP et SCTP, l'ajout d'une adresse IP locale à l'ensemble des adresses utilisables (ADD_PATH.MPTCP et ADD_PATH.SCTP), la notification d'erreurs, l'activation ou la désactivation de la somme de contrôle (UDP seulement, la somme de contrôle étant obligatoire pour les autres)… La plupart sont spécifiques à SCTP, le protocole le plus riche.

La deuxième catégorie rassemble les primitives liées à la transmission de données. On y trouve donc SEND.TCP (idem pour SCTP et UDP), RECEIVE.TCP (avec ses équivalents SCTP et UDP), mais aussi les signalements d'échec comme SEND_FAILURE.UDP par exemple quand on a reçu un message ICMP d'erreur.

Venons-en maintenant à la troisième passe (section 5 du RFC). On va cette fois lister les primitives sous forme d'un concept de haut niveau, en notant les protocoles auxquels elles s'appliquent. Ainsi, pour la gestion des connexions, on aura de quoi se connecter : une primitive Connect, qui s'applique à plusieurs protocoles (TCP, SCTP et UDP, même si Connect ne se réalise pas de la même façon pour tous ces protocoles). Et de quoi écouter passivement : une primitive Listen. Pour la maintenance, on aura une primitive Change timeout, qui s'applique à TCP et SCTP, une Disable Nagle qui s'applique à TCP et SCTP, une Add path qui a un sens pour MPTCP et SCTP, une Disable checksum qui ne marche que pour UDP, etc.

Pour l'envoi de données, on ne fusionne pas les opérations d'envoi (SEND.PROTOCOL) de la passe précédente car elles ont une sémantique trop différente. Il y a Reliably transfer data pour TCP, et Reliably transfer a message pour SCTP. (Et bien sûr Unreliably transfer a message avec UDP.)

Voilà, tout ça a l'air un peu abstrait mais ça acquerra davantage de sens quand on passera aux futures descriptions d'API.

L'annexe B du RFC explique la méthodologie suivie pour l'élaboration de ce document : coller au texte des RFC, exclure les primitives optionnelles, puis les trois passes dont j'ai parlé plus tôt. C'est cette annexe, écrite avant le RFC, qui avait servi de guide aux auteurs des différentes parties (il fallait des spécialistes SCTP pour ne pas faire d'erreurs lors de la description de ce protocole complexe…)

Notez (surtout si vous avez lu le RFC 8095) que TLS n'a pas été inclus, pas parce que ce ne serait pas un vrai protocole de transport (ça se discute) mais car il aurait été plus difficile à traiter (il aurait fallu des experts en sécurité).


Téléchargez le RFC 8303


L'article seul

Les fake news n'existent pas (et c'est vrai)

Première rédaction de cet article le 2 février 2018


Le terme de fake news est à la mode en ce moment. Des sociologues en parlent, les médias s'inquiètent, les politiciens proposent des lois répressives. Pourtant, il n'y a aucun phénomène nouveau, juste le bon vieux mensonge, qui est pratiqué par beaucoup, y compris ceux qui dénoncent vertueusement les fake news.

Déjà, pourquoi en parler en anglais ? Utiliser l'anglais quand des termes parfaits existent en français (selon le cas : mensonge, désinformation, tromperie, propagande), c'est toujours pour brouiller les pistes, pour gêner la réflexion. Ici, le but de ceux qui utilisent cet anglicisme est clair : faire croire qu'il s'agit d'un phénomène nouveau (alors que le mensonge est aussi ancien que la communication), et laisser entendre qu'il est spécifique à l'Internet. Ceux qui brandissent le terme de fake news à tout bout de champ sont en général ceux qui n'ont jamais digéré que l'Internet permette l'accès à d'autres sources d'information.

Les médias qui se veulent officiels ont en effet une classification simple : ce qu'ils écrivent, c'est la vérité, le reste, ce sont des fake news. Regardez par exemple ce titre incroyable sur les « médias légitimes » (les autres sont-ils « illégitimes » ?). Et le reste de l'article est à l'avenant, considérant qu'il n'y a rien entre « médias traditionnels » et « rumeurs ».

Les GAFA, régulièrement accusés par les politistes et par les médias traditionnels, veulent également montrer que la sélection des faits, ils connaissent. Facebook se propose donc de faire la police. Il y a un large accord pour demander un filtrage des informations par les GAFA, et tant pis pour la liberté d'expression.

La diabolisation des fake news pose d'autres problèmes. Par exemple, le problème est souvent présenté de manière binaire : il y a le vrai (les discours du Président de la République, les éditoriaux du Point, les communiqués de la Préfecture de Police) et le faux (le reste). En réalité, entre les mensonges les plus énormes (les armes de destruction massive de Saddam Hussein, par exemple) et les vérités les plus incontestables (le Soleil se lève à l'Est), il y a de la place pour beaucoup de choses, qu'on ne peut pas ranger dans deux catégories bien distinctes. Il y a les faits dont on n'est réellement pas sûrs, ceux où l'analyse est complexe (le « trou de la Sécu »), et des opinions, qui sont variables, sans que certaines soient vraies et d'autres fausses. C'est pour cela que réguler les fake news par la loi (comme exigé par Macron) est dangereux : on passe vite de la lutte contre les fake news à celle contre les opinions qu'on n'aime pas (sans compter le travers bien français de faire une énième loi alors qu'il existe déjà des lois réprimant les fausses nouvelles, comme la loi sur la presse, qui inclut entre autres la protection contre la diffamation).

Mais les mensonges et la désinformation, ça existe bien, non ? Oui, cela existe, et cela existait bien avant l'Internet, Facebook et RT. Mais, d'abord, c'est pratiqué par tous les « camps ». Voir les hommes politiques réclamer une lutte contre les fake news, c'est amusant. Si on interdit les mensonges, les campagnes électorales vont être bien silencieuses. De même, demander qu'on ne croie que l'information officielle n'est pas une solution : les gouvernements peuvent également mentir ou se tromper, et c'est la même chose pour les médias traditionnels. (Le nombre d'énormités qu'on lit dans ces médias dès qu'il s'agit d'un sujet qu'on connaît bien…)

Ensuite, le fait qu'il y ait des mensonges (Sputnik et Breitbart les alignent en quantité impressionnante) ne signifie pas que toute révélation d'un média non-officiel soit un mensonge. De même que l'existence de ridicules complotistes ne veut pas dire qu'il n'existe pas de vrais complots. (Mon exemple favori est l'Iran-Contra Gate.)

Il y a bien sûr de bonnes idées qui circulent dans ce débat, par exemple qu'il est crucial d'avoir une analyse critique de l'information (de toute information, y compris de celle qui vient des médias officiels). L'infographie de l'IFLA est plutôt bien faite. (Notez que la traduction française comportait une énorme erreur, fact-checking traduit par « vérification rapide ». Erreur amusante lorsqu'il s'agit de lutter contre les faux. L'IFLA a modifié cette traduction après la publication de cet article.)

L'important, plutôt que les mouvements de menton (« il faut une loi contre les fake news ») ou que les avis dangereux (« il ne faut croire que l'information officielle ») est de développer les capacités d'analyse critique (« critique » au sens de « penser par soi-même », pas au sens de « jamais d'accord »). Cette capacité d'analyse critique doit s'exercer contre tous les médias et toutes les sources d'information, pas uniquement Internet, habituel grand méchant dans les discours des gens au pouvoir.

Mais en ce qui concerne spécifiquement Internet, il y a aussi des progrès à faire, rentrant dans le cadre général de la littératie numérique. La plus importante me semble la capacité à juger de la provenance de l'information. Si quelqu'un dit « j'ai trouvé cette information sur le Web », c'est clairement un problème (il n'a même pas identifié le site sur lequel il était). Il y a déjà un gros travail à faire en ce sens (lire un URL, comprendre un nom de domaine, distinguer le Web et Facebook…) avant d'espérer un progrès.

Deux jours après la publication de cet article, la ministre de la Culture Officielle a donné un interview où elle confirme que le gouvernement compte bien pouvoir censurer plus rapidement les contenus qui lui déplaisent, et se sert de la censure effectuée par Facebook pour justifier une censure décidée par l'État.

Next Inpact a fait au même moment un bon article sur le sujet des mensonges. Et Johann Savalle‏ a fait un bon résumé en anglais de mon article, sous la forme d'un fil Twitter.

Depuis l'écriture de cet article, je me suis aperçu que, dans le numéro 1329 de Charlie Hebdo, daté du 10 janvier 2018, Guillaume Erner, sous le titre « Jupiter veut foudroyer les fake news » avait dit exactement la même chose, et en mieux. Je promets que je n'ai pas copié (je n'avais pas lu cet article) mais je vous informe que vous pouvez arrêter de lire ce blog et vous abonner à Charlie Hebdo à la place. (Le même Guillaume Erner a fait une excellente démolition du ridicule sondage IFOP « 79 % des Français adhèrent à une théorie complotiste » dans le numéro suivant. Lecture très recommandée, et qui montre bien que « complotisme » fait partie de ces termes flous qui servent essentiellement un but rhétorique.)


L'article seul

RFC 8318: IAB, IESG, and IAOC Selection, Confirmation, and Recall Process: IAOC Advisor for the Nominating Committee

Date de publication du RFC : Janvier 2018
Auteur(s) du RFC : S. Dawkins (Wonder Hamster)
Première rédaction de cet article le 1 février 2018


Un petit RFC bureaucratique pour traiter un manque du RFC 7437, qui ne disait pas que l'IAOC devait envoyer quelqu'un au NomCom, le comité de nomination.

En effet, le NomCom, le comité chargé d'étudier les personnes qui pourront occuper des postes dans divers organismes de l'IETF (cf. RFC 7437), ce NomCom s'occupe entre autres des postes à l'IAOC (RFC 4071), mais ne disait pas que l'IAOC pouvait envoyer un membre au NomCom, comme le faisaient les autres organes. A priori, ce n'était pas grave puisque le NomCom pouvait toujours ajouter qui il voulait comme observateur mais, en 2017, le NomCom a travaillé sur un poste à l'IAOC sans avoir de représentant de l'IAOC à ses réunions.

Ce n'est pas la fin du monde mais cela a justifié ce RFC, qui ajoute juste aux règles existantes que le NomCom devrait veiller à avoir quelqu'un de l'IAOC, ou qui connait l'IAOC.

Les discussions qui ont eu lieu autour de ce minuscule changement sont résumées dans l'annexe A. Par exemple, après un long débat, ce « représentant » de l'IAOC sera qualifié d'« advisor » et pas de « liaison » (en gros, un « liaison » a un boulot plus formel et plus sérieux.)


Téléchargez le RFC 8318


L'article seul

RFC 8314: Cleartext Considered Obsolete: Use of TLS for Email Submission and Access

Date de publication du RFC : Janvier 2018
Auteur(s) du RFC : K. Moore (Windrock), C. Newman (Oracle)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF uta
Première rédaction de cet article le 31 janvier 2018


Ce RFC s'inscrit dans le cadre des efforts de sécurisation de l'Internet contre la surveillance massive, efforts notablement accélérés depuis les révélations Snowden. Désormais, l'usage de texte en clair (non chiffré) pour le courrier électronique est officiellement abandonné : POP, IMAP et SMTP doivent être chiffrés.

Plus précisément, ce nouveau RFC du groupe de travail UTA vise l'accès au courrier (POPRFC 1939 et IMAPRFC 3501), ainsi que la soumission de messages par SMTP - RFC 6409 (entre le MUA et le premier MTA). Les messages de ces protocoles doivent désormais être systématiquement chiffrés avec TLSRFC 5246. Le but est d'assurer la confidentialité des échanges. Le cas de la transmission de messages entre MTA est couvert par des RFC précédents, RFC 3207 et RFC 7672.

Pour résumer les recommandations concrètes de ce RFC :

  • TLS 1.2 au minimum (on croise encore souvent SSL malgré le RFC 7568).
  • Migrer le plus vite possible vers le tout-chiffré si ce n'est pas déjà fait.
  • Utiliser le « TLS implicite » sur un port dédié, où on démarre TLS immédiatement (par opposition au vieux système STARTTLS, où on demandait explicitement le démarrage de la cryptographie, cf. sections 2 et 3 du RFC).

Ce RFC ne traite pas le cas du chiffrement de bout en bout, par exemple avec PGP (RFC 4880 et RFC 3156). Ce chiffrement de bout en bout est certainement la meilleure solution mais il est insuffisamment déployé aujourd'hui. En attendant qu'il se généralise, il faut bien faire ce qu'on peut pour protéger les communications. En outre, PGP ne protège pas certaines métadonnées comme les en-têtes (From:, Subject:, etc), alors qu'un chiffrement TLS du transport le fait. Bref, on a besoin des deux.

La section 3 du RFC rappelle ce qu'est le « TLS implicite », qui est désormais recommandé. Le client TLS se connecte à un serveur TLS sur un port dédié, où tout se fait en TLS, et il démarre la négociation TLS immédiatement. Le TLS implicite s'oppose au « TLS explicite » qui était l'approche initiale pour le courrier. Avec le TLS explicite (RFC 2595 et RFC 3207), le serveur devait annoncer sa capacité à faire du TLS :

% telnet smtpagt1.ext.cnamts.fr. smtp
Trying 93.174.145.55...
Connected to smtpagt1.ext.cnamts.fr.
Escape character is '^]'.
220 smtpagt1.ext.cnamts.fr ESMTP CNAMTS (ain1)
EHLO mail.example.com
250-smtpagt1.ext.cnamts.fr Hello mail.example.com [192.0.2.187], pleased to meet you
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-8BITMIME
250 STARTTLS
    

Et le client devait dire qu'il allait démarrer la session TLS avec la commande STARTTLS. L'inconvénient principal de STARTTLS est qu'il est vulnérable à l'attaque « SSL stripping » où un attaquant actif modifie la communication avant que TLS ne démarre, pour faire croire que le partenaire ne sait pas faire de TLS. (Et il y a aussi la vulnérabilité CERT #555316.) Bien sûr, les serveurs peuvent se protéger en refusant les connexions sans STARTTLS mais peu le font. L'approche STARTTLS était conçue pour gérer un monde où certains logiciels savaient faire du TLS et d'autres pas, mais, à l'heure où la recommandation est de faire du TLS systématiquement, elle n'a plus guère d'utilité. (La question est discutée plus en détail dans l'annexe A. Notez qu'un des auteurs de notre nouveau RFC, Chris Newman, était l'un des auteurs du RFC 2595, qui introduisait l'idée de STARTTLS.)

Avec le TLS implicite, cela donne :

% openssl s_client -connect mail.example.com:465   
...
subject=/CN=mail.example.com
issuer=/O=CAcert Inc./OU=http://www.CAcert.org/CN=CAcert Class 3 Root
...
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
...
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
...
220 mail.example.com ESMTP Postfix
EHLO toto
250-mail.example.com
250-AUTH DIGEST-MD5 NTLM CRAM-MD5
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250 SMTPUTF8
    

Donc, concrètement, pour POP, cela veut dire établir la connexion sur le port du TLS implicite, le 995 (et non pas sur le port 110, prévu pour le texte en clair), lancer TLS et authentifier avec le RFC 7817. Puis on fait du POP classique. Pour IMAP, c'est le port 993. Dans les deux cas, cette recommandation de notre RFC ne sera pas trop dure à suivre, le TLS implicite est déjà courant pour ces deux protocoles.

Pour la soumission SMTP (RFC 6409), c'est le port 465 (utilisé auparavant pour du SMTP classique, non-soumission, cf. le registre IANA et les section 7.3 et annexe A du RFC qui justifient cette réaffectation). Le mécanisme avec STARTTLS sur le port 587 (TLS explicite) est très répandu, contrairement à ce qui se passe pour POP et IMAP. La transition sera donc plus longue, et il faudra donc maintenir les deux services, TLS implicite et explicite, pendant un certain temps.

Voici les serveurs IMAP (pour récupérer le courrier) et SMTP (pour soumettre du courrier) conformes aux recommandations de ce RFC (TLS implicite), tels qu'affichés par Thunderbird : thunderbird-servers-ports.png

Et voici la configuration du MTA Postfix pour accepter du TLS implicite sur le port 465 :

submissions inet n       -       -       -       -       smtpd
   -o syslog_name=postfix/submissions
   -o smtpd_tls_wrappermode=yes
   -o smtpd_sasl_auth_enable=yes
   -o smtpd_client_restrictions=permit_sasl_authenticated,reject
   -o smtpd_etrn_restrictions=reject
   -o smtpd_sasl_authenticated_header=yes
    

(À mettre dans le master.cf.) Cette configuration active TLS et exige une authentification du client (ce qui est normal pour soumettre du courrier.) Pensez aussi à vérifier que le port est bien défini dans /etc/services (smtps 465/tcp ssmtp submissions).

La section 4 du RFC fournit des détails sur l'utilisation de TLS, du côté des fournisseurs de service de courrier. Le point essentiel est « chiffrement partout, tout le temps » (section 4.1). L'objectif ne pourra pas forcément être atteint immédiatement par tout le monde mais il faut commencer. Par exemple, on peut interdire tout accès en texte clair à certains utilisateurs, puis généraliser à tous. Et, dans ce cas, le message envoyé doit être indépendant de si le mot de passe était valide ou pas (pour ne pas donner d'indication à un éventuel écoutant). Le RFC conseille aussi de traiter les accès avec les vieilles versions de TLS (ou, pire, avec SSL) de la même façon que les accès en clair. Une fois l'accès en clair coupé, il est recommandé de forcer un changement de mot de passe, si l'ancien avait été utilisé en clair.

Et les autres points ? Les fournisseurs de services de courrier électronique doivent annoncer les services POP, IMAP et soumission SMTP en utilisant les enregistrements SRV du RFC 6186, afin de faciliter la tâche des MUA. Un nouvel enregistrement SRV arrive avec ce RFC, d'ailleurs, le _submissions._tcp, pour la soumission SMTP sur TLS. Le RFC demande que ces enregistrements SRV (ainsi que les enregistrements MX utilisés pour le courrier entrant, donc a priori pas le sujet de ce RFC) soient signés avec DNSSEC. Et, à propos du DNS, le RFC demande également la publication d'enregistrements TLSA (RFC 6698).

La cryptographie, c'est bien, mais il est également souhaitable de signaler qu'elle a été utilisée, et dans quelles conditions, pour informer les utilisateurs de la sécurité dont ils ont pu bénéficier. Par exemple, le RFC demande que les algorithmes de cryptographie utilisées soient mis dans l'en-tête Received: du courrier (cf. aussi le RFC 3848), via une clause tls (dans registre IANA). Notez que beaucoup de serveurs SMTP le font déjà, avec une syntaxe différente :

      
Received: from mail4.protonmail.ch (mail4.protonmail.ch [185.70.40.27])
        (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
        (No client certificate requested)
        by mail.bortzmeyer.org (Postfix) with ESMTPS id 38DAF31D16
        for <stephane@bortzmeyer.org>; Sat, 20 Jan 2018 16:56:59 +0100 (CET)

      

La section 5 décrit les exigences pour l'autre côté, le client, cette fois, et non plus le serveur de la section 4. D'abord, comme les MUA sont en contact direct avec l'utilisateur humain, ils doivent lui indiquer clairement le niveau de confidentialité qui a été obtenu (par exemple par une jolie icône, différente si on a utilisé TLS ou pas). Notez que cette indication de la confidentialité est un des points du projet Caliopen. Gmail, par exemple, peut afficher ces informations et cela donne :gmail-tls.png

Comment est-ce qu'un MUA doit trouver s·on·es serveur·s, au fait ? La méthode recommandée est d'utiliser les enregistements SRV du RFC 6186. Aux _imap._tcp et _pop3._tcp du RFC précédent s'ajoute _submissions._tcp pour le SMTP de soumission d'un message, avec TLS implicite. Attention, le but étant la confidentialité, le MUA ne devrait pas utiliser les serveurs annoncés via les SRV s'ils ne satisfont pas à des exigences minimales de sécurité TLS. Le MUA raisonnable devrait vérifier que le SRV est signé avec DNSSEC, ou, sinon, que le serveur indiqué est dans le même domaine que le domaine de l'adresse de courrier. La section 5.2 donne d'autres idées de choses à vérifier (validation du certificat du serveur suivant le RFC 7817, de la version de TLS…) D'une manière générale, le MUA ne doit pas envoyer d'informations sensibles comme un mot de passe si la session n'est pas sûre. La tentation pourrait être d'afficher à l'utilisateur un dialogue du genre « Nous n'avons pas réussi à établir une connexion TLS satisfaisante car la version de TLS n'est que la 1.1 et le groupe Diffie-Hellman ne fait que 512 bits, voulez-vous continuer quand même ? » Mais le RFC s'oppose à cette approche, faisant remarquer qu'il est alors trop facile pour l'utilisateur de cliquer OK et de prendre ainsi des risques qu'il ne maitrise pas.

Autre question de sécurité délicate, l'épinglage du certificat. Il s'agit de garder un certificat qui a marché autrefois, même s'il ne marche plus, par exemple parce qu'expiré. (Ce n'est donc pas le même épinglage que celui qui est proposé pour HTTP par le RFC 7469.) Le RFC autorise cette pratique, ce qui est du bon sens : un certificat expiré, ce n'est pas la même chose qu'un certificat faux. Et ces certificats expirés sont fréquents car, hélas, bien des administrateurs système ne supervisent pas l'expiration des certificats. Voici la configuration d'Icinga pour superviser un service de soumission via SMTP :

      
apply Service "submissions" {
  import "generic-service"

  check_command = "ssmtp"
  vars.ssmtp_port = 465
  assign where (host.address || host.address6) && host.vars.submissions
  vars.ssmtp_certificate_age = "7,3"
}

    

Et, une fois ce service défini, on peut ajouter à la définition d'un serveur vars.submissions = true et il sera alors supervisé : icinga-submissions.png

Notre RFC recommande également aux auteurs de MUA de faire en sorte que les utilisateurs soient informés du niveau de sécurité de la communication entre le client et le serveur. Tâche délicate, comme souvent quand on veut communiquer avec les humains. Il ne faut pas faire de fausses promesses (« votre connection est cryptée avec des techniques military-grade, vous êtes en parfaite sécurité ») tout en donnant quand même des informations, en insistant non pas sur la technique (« votre connexion utilise ECDHE-RSA-AES256-GCM-SHA384, je vous mets un A+ ») mais sur les conséquences (« Ce serveur de courrier ne propose pas de chiffrement de la communication, des dizaines d'employés de la NSA, de votre FAI, et de la Fsociety sont en train de lire le message où vous parlez de ce que vous avez fait hier soir. »).

Voilà, vous avez l'essentiel de ce RFC. Quelques détails, maintenant. D'abord, l'interaction de ces règles avec les antivirus et antispam. Il y a plusieurs façons de connecter un serveur de messagerie à un logiciel antivirus et·ou antispam (par exemple l'interface Milter, très répandue). Parfois, c'est via SMTP, avec l'antivirus et·ou antispam qui se place sur le trajet, intercepte les messages et les analyse avant de les retransmettre. C'est en général une mauvaise idée (RFC 2979). Dès qu'il y a du TLS dans la communication, c'est encore pire. Puisque le but de TLS est de garantir l'authenticité et l'intégrité de la communication, tout « intercepteur » va forcément être très sale. (Et les logiciels qui font cela sont d'abominables daubes.)

Ah, et un avertissement lié à la vie privée (section 8 du RFC) : si on présente un certificat client, on révèle son identité à tout écoutant. Le futur TLS 1.3 aidera peut-être à limiter ce risque mais, pour l'instant, attention à l'authentification par certificat client.

Si vous aimez connaitre les raisons d'un choix technique, l'annexe A couvre en détail les avantages et inconvénients respectifs du TLS implicite sur un port séparé et du TLS explicite sur le port habituel. La section 7 du RFC 2595 donnait des arguments en faveur du TLS explicite. Certaines des critiques que ce vieux RFC exprimait contre le TLS implicite n'ont plus de sens aujourd'hui (le RFC 6186 permet que le choix soit désormais fait par le logiciel et plus par l'utilisateur, et les algorithmes export sont normalement abandonnés). D'autres critiques ont toujours été mauvaises (par exemple, celle qui dit que le choix entre TLS et pas de TLS est binaire : un MUA peut essayer les deux.) Outre la sécurité, un avantage du port séparé pour TLS est qu'il permet de déployer des frontaux - répartiteurs de charge, par exemple - TLS génériques.

Et pour terminer, d'autres exemples de configuration. D'abord avec mutt, par Keltounet :

      smtp_url=smtps://USER@SERVER:465
    

Et le courrier est bien envoyé en TLS au port de soumission à TLS implicite.

Avec Dovecot, pour indiquer les protocoles utilisés dans la session TLS, Shaft suggère, dans conf.d/10-logging.conf :

login_log_format_elements = user=<%u> method=%m rip=%r lip=%l mpid=%e %c %k
    

(Notez le %k à la fin, qui n'est pas dans le fichier de configuration d'exemple.)

Et pour OpenSMTPd ? n05f3ra1u propose :

pki smtp.monserveur.example key "/etc/letsencrypt/live/monserveur.example/privkey.pem"
pki smtp.monserveur.example certificate "/etc/letsencrypt/live/monserveur.example/cert.pem"
...
listen on eno1 port 465 hostname smtp.monserveur.example smtps pki smtp.monserveur.example auth mask-source
    

Téléchargez le RFC 8314


L'article seul

RFC 2818: HTTP Over TLS

Date de publication du RFC : Mai 2000
Auteur(s) du RFC : E. Rescorla (RTFM)
Pour information
Première rédaction de cet article le 27 janvier 2018


C'est un RFC très ancien que ce RFC 2818, et qui décrit une technologie qui ne s'est pourtant imposée presque partout que récemment, HTTPS (HTTP sur TLS). Le RFC n'a pas été remplacé depuis sa publication en… 2000, même si le RFC 7230 a apporté quelques changements.

Remettons-nous dans le contexte de l'époque : en 2000, la NSA espionnait déjà massivement, mais on n'avait pas encore eu les révélations Snowden. Les pays comme la Russie et la Chine n'avaient sans doute pas encore d'activités d'espionnage sur l'Internet et la France… espionnait le trafic Minitel. L'inquiétude à l'époque n'était pas tellement tournée vers les services secrets officiels mais plutôt vers le méchant pirate qui allait copier les numéros de carte bleue quand on allait les utiliser pour le e-commerce (concept relativement récent à l'époque). HTTPS, le HTTP sécurisé par la cryptographie semblait à l'époque réservé aux formulaires où on tapait un numéro de carte de crédit. Ce n'est qu'à partir des révélations de Snowden que HTTPS a commencé à être déployé systématiquement, et qu'il forme sans doute aujourd'hui la majorité du trafic, malgré les indiscrets de tout bord qui auraient voulu continuer à surveiller le trafic, et qui répétaient « M. Michu n'a pas besoin de chiffrement ». (Au FIC en 2016, dans une réunion publique, un représentant d'un grand FAI se désolait que le déploiement massif de HTTPS empêchait d'étudier ce que faisaient ses clients…) Ce succès est en partie dû à des campagnes de sensibilisation (menées par exemple par l'EFF) et à des outils comme HTTPS Everywhere.

Revenons au RFC. Il est très court (sept pages) ce qui est logique puisque le gros du travail est fait dans le RFC 5246, qui normalise TLS (c'était le RFC 2246, à l'époque). Car, HTTPS, c'est juste HTTP sur TLS, il n'y a pas grand'chose à spécifier. Notons que le titre du RFC est « HTTP sur TLS », que le texte ne parle que de TLS comme protocole de cryptographie et pourtant, dix-huit ans plus tard, des gens ignorants parlent encore de SSL, le prédécesseur, abandonné depuis longtemps, de TLS.

Ironie des processus internes de l'IETF, notre RFC n'est pas sur le chemin des normes Internet, il a seulement la qualité « pour information ».

La section 1 résume le RFC : HTTPS, comme TLS, protège le canal et non pas les données. Cela veut dire notamment que HTTPS ne protège pas si on a des sites miroir et que l'un d'eux se fait pirater. (En 2018, on n'a toujours pas de moyen standard de sécuriser les données envoyées sur le Web, malgré quelques briques possiblement utilisables comme XML SignatureRFC 3275 et JOSE.)

La section 2 décrit le protocole en une phrase : « Utilisez HTTP sur TLS, comme vous utiliseriez HTTP sur TCP. » Bon, j'exagère, il y a quelques détails. Le client HTTPS doit être un client TLS, donc un client TCP. Il se connecte au serveur en TCP puis lance TLS puis fait des requêtes HTTP par dessus la session TLS, elle-même tournant sur la connexion TCP. Cela se voit bien avec l'option -v de curl. D'abord du TCP :


% curl -v https://www.bortzmeyer.org/crypto-protection.html 
*   Trying 2605:4500:2:245b::42...
* TCP_NODELAY set
* Connected to www.bortzmeyer.org (2605:4500:2:245b::42) port 443 (#0)

Puis TLS démarre :


* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: CN=www.bortzmeyer.org
*  start date: Sep 21 06:34:08 2016 GMT
*  expire date: Sep 21 06:34:08 2018 GMT
*  subjectAltName: host "www.bortzmeyer.org" matched cert's "www.bortzmeyer.org"
*  issuer: O=CAcert Inc.; OU=http://www.CAcert.org; CN=CAcert Class 3 Root
*  SSL certificate verify ok.

Et enfin on peut faire de l'HTTP :


> GET /crypto-protection.html HTTP/1.1
> Host: www.bortzmeyer.org
> User-Agent: curl/7.52.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: Apache/2.4.25 (Debian)
< ETag: "8ba9-56281c23dd308"
< Content-Length: 35753
< Content-Type: text/html
< 
<?xml version="1.0" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xml:lang="fr" lang="fr" xmlns="http://www.w3.org/1999/xhtml">
<head>
...
<title>Blog Stéphane Bortzmeyer: La cryptographie nous protège t-elle vraiment de l'espionnage par la NSA ou la DGSE ?</title>
...
</body>
</html>

Et on termine :

  
* Curl_http_done: called premature == 0
* Connection #0 to host www.bortzmeyer.org left intact

Si on n'aime pas curl, on peut le faire avec le client en ligne de commande de GnuTLS, puis taper les requêtes HTTP à la main :


% echo -n "GET /who-buys-porn.html HTTP/1.1\r\nHost: www.bortzmeyer.org\r\nUser-Agent: à la main\r\n\r\n" |  \
  gnutls-cli www.bortzmeyer.org
Processed 167 CA certificate(s).
Resolving 'www.bortzmeyer.org:443'...
Connecting to '2605:4500:2:245b::42:443'...
- Certificate type: X.509
- Got a certificate list of 2 certificates.
- Certificate[0] info:
 - subject `CN=www.bortzmeyer.org', issuer `CN=CAcert Class 3 Root,OU=http://www.CAcert.org,O=CAcert Inc.', serial 0x029ba3, RSA key 2048 bits, signed using RSA-SHA256, activated `2016-09-21 06:34:08 UTC', expires `2018-09-21 06:34:08 UTC', key-ID `sha256:141954e99b9c88a33af6ffc00db09f5d8b66185a7325f45cbd45ca3cb47d63ce'
	Public Key ID:
		sha1:82500582b33c12ada8891e9a36192131094bed22
		sha256:141954e99b9c88a33af6ffc00db09f5d8b66185a7325f45cbd45ca3cb47d63ce
	Public key's random art:
		+--[ RSA 2048]----+
		|o=o oo.          |
		|O..+             |
		|+Bo              |
		|E= o .           |
		|*.+ . . S        |
		|+o     .         |
		|o.+              |
		|o=               |
		|. .              |
		+-----------------+

- Certificate[1] info:
 - subject `CN=CAcert Class 3 Root,OU=http://www.CAcert.org,O=CAcert Inc.', issuer `EMAIL=support@cacert.org,CN=CA Cert Signing Authority,OU=http://www.cacert.org,O=Root CA', serial 0x0a418a, RSA key 4096 bits, signed using RSA-SHA256, activated `2011-05-23 17:48:02 UTC', expires `2021-05-20 17:48:02 UTC', key-ID `sha256:bd0d07296b43fae03b64e650cbd18f5e26714252035189d3e1263e4814b4da5a'
- Status: The certificate is trusted. 
- Description: (TLS1.2)-(ECDHE-RSA-SECP256R1)-(AES-256-GCM)
- Session ID: A6:A6:CE:32:08:9D:B8:D2:EF:D6:C7:D4:85:B4:39:D7:0D:04:83:72:6F:87:02:50:44:B7:29:64:E5:69:0B:07
- Ephemeral EC Diffie-Hellman parameters
 - Using curve: SECP256R1
 - Curve size: 256 bits
- Version: TLS1.2
- Key Exchange: ECDHE-RSA
- Server Signature: RSA-SHA256
- Cipher: AES-256-GCM
- MAC: AEAD
- Compression: NULL
- Options: safe renegotiation,
- Handshake was completed

- Simple Client Mode:

HTTP/1.1 200 OK
Server: Apache/2.4.25 (Debian)
Content-Length: 8926
Content-Type: text/html

Et, vu par tshark, ça donne d'abord TCP :

1 0.000000000 2a01:e35:8bd9:8bb0:dd2c:41a5:337f:a21f → 2605:4500:2:245b::42 TCP 94 45502 → 443 [SYN] Seq=0 Win=28400 Len=0 MSS=1420 SACK_PERM=1 TSval=26014765 TSecr=0 WS=128
2 0.080848074 2605:4500:2:245b::42 → 2a01:e35:8bd9:8bb0:dd2c:41a5:337f:a21f TCP 94 443 → 45502 [SYN, ACK] Seq=0 Ack=1 Win=28560 Len=0 MSS=1440 SACK_PERM=1 TSval=476675178 TSecr=26014765 WS=64
3 0.080902511 2a01:e35:8bd9:8bb0:dd2c:41a5:337f:a21f → 2605:4500:2:245b::42 TCP 86 45502 → 443 [ACK] Seq=1 Ack=1 Win=28416 Len=0 TSval=26014785 TSecr=476675178

Puis TLS :

4 0.084839464 2001:db8:57aa:8bb0:dd2c:9af:222f:a21f → 2605:4500:2:245b::42 SSL 351 Client Hello
5 0.170845913 2605:4500:2:245b::42 → 2001:db8:57aa:8bb0:dd2c:9af:222f:a21f TCP 86 443 → 45502 [ACK] Seq=1 Ack=266 Win=29632 Len=0 TSval=476675200 TSecr=26014786
6 0.187457346 2605:4500:2:245b::42 → 2001:db8:57aa:8bb0:dd2c:9af:222f:a21f TLSv1.2 1494 Server Hello
7 0.187471721 2001:db8:57aa:8bb0:dd2c:9af:222f:a21f → 2605:4500:2:245b::42 TCP 86 45502 → 443 [ACK] Seq=266 Ack=1409 Win=31232 Len=0 TSval=26014812 TSecr=476675202
8 0.187583400 2605:4500:2:245b::42 → 2001:db8:57aa:8bb0:dd2c:9af:222f:a21f TLSv1.2 2320 Certificate, Server Key Exchange, Server Hello Done
9 0.187600261 2001:db8:57aa:8bb0:dd2c:9af:222f:a21f → 2605:4500:2:245b::42 TCP 86 45502 → 443 [ACK] Seq=266 Ack=3643 Win=35712 Len=0 TSval=26014812 TSecr=476675202
10 0.199416730 2001:db8:57aa:8bb0:dd2c:9af:222f:a21f → 2605:4500:2:245b::42 TLSv1.2 212 Client Key Exchange, Change Cipher Spec, Hello Request, Hello Request
11 0.283191938 2605:4500:2:245b::42 → 2001:db8:57aa:8bb0:dd2c:9af:222f:a21f TLSv1.2 360 New Session Ticket, Change Cipher Spec, Encrypted Handshake Message

C'est fait, TLS a démarré (notez le nombre de paquets qu'il a fallu échanger pour cela : TLS accroit la latence, et beaucoup de travaux actuellement à l'IETF visent à améliorer ce point). On peut maintenant faire du HTTP :

12 0.283752212 2001:db8:57aa:8bb0:dd2c:9af:222f:a21f → 2605:4500:2:245b::42 TLSv1.2 201 Application Data
13 0.365992864 2605:4500:2:245b::42 → 2001:db8:57aa:8bb0:dd2c:9af:222f:a21f TLSv1.2 1494 Application Data

Comment, on ne voit pas le HTTP, juste le Application Data générique de TLS ? Oui, c'est exprès, c'est bien à ça que sert TLS.

Bon, ce n'est pas tout d'ouvrir une session, il faut aussi la fermer. Le RFC précise qu'il faut utiliser la fermeture TLS avant la fermeture TCP, pour être sûr que toutes les données ont été transmises. Le cas des fermetures de session trop brutales forme d'ailleurs l'essentiel du RFC (que faire si la connexion TCP est rompue avant la session TLS ? Cela peut avoir des conséquences de sécurité, par exemple s'il n'y a pas d'en-tête HTTP Conent-Length: et que la connexion TCP est coupée, on ne peut pas savoir si on a tout reçu ou bien si un attaquant a généré une fausse coupure de connexion TCP qui, contrairement à la fermeture TLS, n'est pas authentifiée, cf. RFC 5961.

Reste la question du port à utiliser. Comme vous le savez sans doute, HTTPS utilise le port 443. Il est enregistré à cet effet à l'IANA. Notez que gnutls-cli, utilisé plus haut, utilise ce port par défaut.

Et le plan d'URI à utiliser pour HTTPS ? Là aussi, vous le connaissez, c'est https:// donc par exemple, https://www.afnic.fr/, https://www.ietf.org/blog/codel-improved-networking-through-adaptively-managed-router-queues/ ou https://lad.wikipedia.org/wiki/Gefilte_fish. Il est également enregistré à l'IANA. Notez que le RFC 7230 a apporté, depuis, quelques changements (sans toutefois remplacer complètement le vieux RFC 2818). Le principal est que la définition de ce plan https:// pour les URI est désormais dans le RFC 7230, et plus détaillée que la description informelle du vieux RFC.

Mais ça n'est pas tout d'avoir une connexion chiffrée… Comme le notent plusieurs auteurs « TLS permet d'avoir une session sécurisée avec un ennemi ». En effet, si vous n'authentifiez pas la machine à l'autre bout, TLS ne sera pas très utile. Vous risquerez d'être victime de l'attaque de l'homme du milieu où Mallory se place entre Alice et Bob, prétendant être Bob pour Alice et Alice pour Bob. Il est donc très important d'authentifier le partenaire, et c'est là la principale faiblesse de HTTPS. On utilise en général un certificat X.509 (ou, plus rigoureusement, un certificat PKIX, cf. RFC 5280). Comme HTTPS part en général d'un URI, le client a dans cet URI le nom de domaine du serveur. C'est ce nom qu'il doit comparer avec ce qu'il trouve dans le certificat (plus exactement, on regarde le subjectAltName si présent, puis le Common Name, CN, mais rappelez-vous que le RFC est vieux, les règles ont évolué depuis). D'autres techniques d'authentification sont possibles, comme l'épinglage de la clé (RFC 7469), qu'on peut combiner avec les clés nues du RFC 7250.

Si quelque chose ne va pas dans l'authentification, on se récupère un avertissement du navigateur, proposant parfois de passer outre le problème. Le RFC notait déjà, il y a dix-huit ans, que c'était dangereux car cela laisserait la session potentiellement sans protection. Globalement, depuis la sortie du RFC, les navigateurs sont devenus de plus en plus stricts, rendant ce débrayage de la sécurité plus difficile.

Voici un exemple de problème d'authentification. Le Centre Hospitalier de Bois-Petit redirige automatiquement les visiteurs vers la version HTTPS de son site, qui n'a pas le bon nom dans le certificat. Ici, la protestation de Firefox (notez la possibilité d'ajouter une exception manuellement) : ch-boispetit-tls.png

curl râle également, bien sûr :

% curl https://www.ch-boispetit.fr/ 
curl: (51) SSL: no alternative certificate subject name matches target host name 'www.ch-boispetit.fr'
  

Une variante de cette erreur se produit lorsque le site est accessible par plusieurs noms en HTTP mais que tous ne sont pas authentifiables en HTTPS. Par exemple, pour le quotidien Le Monde, http://lemonde.fr et http://www.lemonde.fr marchent mais, en HTTPS, seul www.lemonde.fr est dans le certificat. (Même chose, aujourd'hui, pour https://wikipedia.fr/.) D'où le refus de Chromium : lemonde-tls.png

D'autres erreurs sont possibles avec la gestion des certificats, la plus fréquente étant sans doute le certificat expiré. Si vous voulez regarder avec votre navigateur Web des exemples d'erreurs, l'excellent site https://badssl.com/ vous en propose plein, faites exprès.

Le RFC note à juste titre que l'URI de départ vient souvent d'une source qui n'est pas digne de confiance. Si vous suivez un lien qui est dans vos signets, ou que vous le tapez vous-même en faisant attention, l'URI est digne de confiance, et la vérification du certificat vous protégera. Mais si vous cliquez sur un lien dans un message envoyé par une nommée natacha@viagra-pharmacy.ru annonçant « Agrandissez votre pénis », ou même un lien dans un message prétendant venir de votre patron et annonçant « dossier très urgent à lire » (rappelez-vous que le courrier électronique n'est pas authentifié, sauf si on utilise PGP ou équivalent), alors, l'URI n'est pas sûr et HTTPS ne vous protégera pas (sauf si vous scrutez avec soin l'URI dans la barre d'adresses du navigateur, ce que personne ne fait et, de toute façon, c'est parfois trop tard). C'est ce qu'on appelle le hameçonnage et c'est une attaque fréquente, contre laquelle toute la cryptographie du monde ne vous protège pas. (Ceci dit, à l'heure actuelle, spammeurs et hameçonneurs ne se sont pas mis massivement à HTTPS, la plupart du temps, ils en restent au bon vieil HTTP.)

Notez qu'on n'a parlé que de la vérification par le client de l'identité du serveur (qui utilise le fait que le client connait le nom de domaine du serveur). La vérification par le serveur de l'identité du client est également possible (envoi d'un certificat par le client) mais elle nécessite que le serveur ait une base des clients connus (ou qu'il accepte tous les certificats d'une AC donnée).

Après cet article, et compte tenu du fait que HTTPS existe formellement depuis si longtemps, vous devez vous dire que ce blog que vous lisez est accessible en HTTPS, non ? Eh bien, il l'est mais je ne publie pas les URI en https:// et je ne fais pas de redirection automatique vers la version HTTPS. En effet, j'utilise une AC gratuite et facile d'usage mais que la plupart des vendeurs n'incluent pas dans leur magasin des AC (ce qui est une décision arbitraire : regardez le magasin des AC via votre navigateur Web, et voyez si vous faites confiance aux gouvernements chinois et turcs, ainsi qu'aux entreprises privées qui ne pensent qu'au profit). Tant que ce problème durera, je ne pourrais pas faire de HTTPS par défaut. En attendant, si vous voulez voir ce blog sur HTTPS, sur une Debian, faites juste sudo aptitude install ca-cacert (puis redémarrez le navigateur), sur les autres systèmes, allez en https://www.cacert.org/index.php?id=3 et installez le certificat de CAcert.

Au fait, si quelqu'un a des références de bonnes études quantitatives sur le déploiement de HTTPS entre 2000 et aujourd'hui, je suis preneur. Je connais pour l'instant la télémétrie de Firefox. Elle est affichée ici (mais malheureusement uniquement depuis 2014) et montre aujourd'hui 70 % de pages Web en HTTPS.


Téléchargez le RFC 2818


L'article seul

Les spammeurs ne sont même pas compétents en standards du courrier

Première rédaction de cet article le 20 janvier 2018


Comme vous, je reçois du spam via le courrier électronique. J'en reçois peut-être davantage car mon adresse publiée sur ce blog a manifestement été mise sur des listes d'« influenceurs » et autres blogueurs mode. Ces spams, plus orientés « professionnels » sont souvent en deux parties, une en texte seul et l'autre en HTML. Mais ce qui est amusant est que les deux parties sont parfois désynchronisées.

[Si vous connaissez bien MIME, vous pouvez sauter l'essentiel de cet article, et aller directement aux observations de la fin. J'explique d'abord MIME pour les gens qui ne connaissent pas.] Cette possibilité d'envoyer un message en plusieurs parties est normalisée dans le standard dit MIME, spécifié dans le RFC 2045. Il existe de nombreuses possibilités de message en plusieurs parties dans MIME dont les deux plus connues sont les parties différentes (multipart/mixed dans les étiquettes MIME) et les parties alternatives (multipart/alternative pour MIME). Bien sûr, la plupart des gens qui envoient des newsletter et CP ne savent pas cela. Ils ignorent que les messages ont une forme normalisée dans le RFC 5322 et qui est assez différente de ce qu'ils voient sur leur écran. Ainsi, prenons un message qui ressemble à ceci avec Thunderbird : thunderbird-mime.png

Sur le réseau, il sera très différent. Ce qui passe, et qui est interprété par Thunderbird, sera :


Date: Sat, 20 Jan 2018 17:07:29 +0100
From: Stephane Bortzmeyer <stephane@bortzmeyer.org>
To: johndoe@example.com
Subject: Les roux, c'est sympa
Message-ID: <20180120160727.ehuvwpqhrhbzpv26@bortzmeyer.org>
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="gr5ck6jng3husyqz"
User-Agent: NeoMutt/20170113 (1.7.2)


--gr5ck6jng3husyqz
Content-Type: text/plain; charset=utf-8
Content-Disposition: inline
Content-Transfer-Encoding: 8bit

Un potamochère roux au ZooParc de Beauval.
Date	       15 May 2016, 16:03:54
Source	       Own work
Author	       Thesupermat

--gr5ck6jng3husyqz
Content-Type: image/jpeg
Content-Disposition: attachment; filename*=utf-8''Zooparc_de_Beauval_-_Potamoch%C3%A8re_roux_-_2016_-_002%2Ejpg
Content-Transfer-Encoding: base64

/9j/4AAQSkZJRgABAgEBLAEsAAD/4UnURXhpZgAASUkqAAgAAAAMAA8BAgAGAAAAngAAABAB
AgAVAAAApAAAABIBAwABAAAAAQAAABoBBQABAAAAugAAABsBBQABAAAAwgAAACgBAwABAAAA
AgAAADEBAgAVAAAAygAAADIBAgAUAAAA4AAAADsBAgAMAAAA9AAAAJiCAgAJAAAAAAEAAGmH
...
   
    

Les parties différentes sont utilisées pour le cas où on envoie, par exemple, un texte et une image d'illustration (ce qui est le cas de l'exemple ci-dessus). Elles sont décrites dans le RFC 2046, section 5.1.3. Le message a un en-tête du genre (regardez l'exemple plus haut) :

    
Content-Type: multipart/mixed; boundary="------------514DC64B3521BD0334B199AA"

Une autre possibilité courante de message en plusieurs parties est le cas des parties alternatives (RFC 2046, section 5.1.4). Prenons ce spam typique : thunderbird-mime-2.png

Sur le réseau, le contenu avait en fait deux parties, une en texte brut et une en HTML. Normalement, les deux sont équivalentes, et le MUA va choisir laquelle afficher. Dans l'image ci-dessus, Thunderbird affichait le HTML. Ici, un autre MUA, mutt fait un autre choix et affiche le texte seul : mutt-mime.png

Quant à ce qui circulait sur le câble, cela ressemble à :


MIME-Version: 1.0
To: bortzmeyer+ietf@nic.fr
Content-Type: multipart/alternative;
	boundary="----=_NextPart_001_322D_019C6BD8.657D49B2"
X-Mailer: Smart_Send_2_0_138
Date: Thu, 18 Jan 2018 08:49:31 -0500
Message-ID: <5860441356088315066310@Ankur>
X-SMTPCOM-Tracking-Number: f5f27516-49b6-40ee-848b-62cb2597a453
X-SMTPCOM-Sender-ID: 6008902
Feedback-ID: 6008902:SMTPCOM
Subject: The SDN, NFV & Network Virtualization Ecosystem: 2017 - 2030 - Opportunities, Challenges, Strategies & Forecasts (Report
)
From: "Andy Silva" <andy.silva@snsreports.com>


------=_NextPart_001_322D_019C6BD8.657D49B2
Content-Type: text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

The SDN, NFV & Network Virtualization Ecosystem: 2017 =96 2030 =96 Opportun=
ities, Challenges, Strategies & Forecasts

...

------=_NextPart_001_322D_019C6BD8.657D49B2
Content-Type: text/html; charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

<HTML xmlns:o><HEAD>
<META content=3D"text/html; charset=3Diso-8859-1" http-equiv=3DContent-Type>
<META name=3DGENERATOR content=3D"MSHTML 11.00.10570.1001"></HEAD>
<BODY>
<P><SPAN><FONT color=3D#000000><FONT color=3D#000000><FONT color=3D#000000>=
...
      
    

Enfin, il existe d'autres types de messages en plusieurs parties, comme les possibilités multilingues du RFC 8255.

Voilà, tout cela est ancien (le RFC 2046 date de 1996 !) Maintenant, passons aux observations récentes. J'ai reçu le 15 janvier 2018 un spam de l'agence Maatch annonçant un « Voyage de presse Roanne Tout & Simplement » (un voyage de presse est un événement où une enterprise nourrit et abreuve des « influenceurs » pour qu'ils écrivent ensuite des articles favorables). Bizarrement, le voyage est annoncé pour le 9 septembre 2016. Et le message est « signé » d'un nom différent de celui utilisé dans le champ From:. Il se trouve que je lis mon courrier avec mutt et l'option alternative_order text/plain qui lui indique de préférer, dans les messages composés de plusieurs parties alternatives, le texte seul. Si je regarde la partie HTML du message, je vois la bonne date (31 janvier 2018), le bon programme (« invitation au restaurant gastronomique Le Central, en compagnie de l'équipe de Roanne Tout & Simplement et de chefs d'entreprises numériques ») et la bonne « signature ». La personne qui a préparé le message n'a sans doute pas directement écrit au format IMF (Internet Message Format, RFC 5322) mais a utilisé un logiciel qui n'affichait que la partie HTML, la seule qu'elle met à jour quand elle prépare un nouvel envoi. Toutes les occurrences futures de ce voyage de presse seront donc envoyés avec une partie en texte brut restée figée en 2016. Et personne ne s'en est aperçu car l'agence ne teste pas ses messages avec divers MUA et que les destinataires, soit sont mariés avec les logiciels « HTML seulement », soit envoient automatiquement ce genre de messages dans le dossier Spam.

Le choix de l'utilisateur, présenté par mutt : mutt-choix.png

Est-ce un cas isolé dû à une agence de communication particulièrement incompétente ? Non, j'ai regardé d'autres spams et le phénomène ne semble pas exceptionnel. Par exemple, le 18 janvier, je reçois un spam de l'agence Srvsaas/Sellinity intitulé « Invitation personnelle: Dîner privilégié » et c'est encore plus amusant. La partie texte décrit un diner avec les commerciaux d'un fabricant de matériel réseau bien connu, la partie HTML un diner avec les commerciaux d'une autre entreprise, une entreprise de sécurité informatique importante. L'agence a repris un message conçu pour un client, a modifié la partie HTML pour un autre client et a oublié la partie texte !

D'autres accidents sont possibles avec ces deux parties pas toujours synchronisées. Par exemple, on me signale que les messages de confirmation de Ryanair ont une partie texte et une partie HTML et que la partie texte contient… du HTML. Bref, regarder la partie texte et la partie HTML des spams peut être une distraction intéressante.


L'article seul

Noms de domaine, DNS et vie privée

Première rédaction de cet article le 14 janvier 2018


Le 13 janvier 2018, à Fontenay-le-Fleury, à l'invitation de l'association de libristes Root66, j'ai fait un exposé sur les noms de domaine, le DNS et la vie privée. Presque trois heures, ouf (mais une galette après).

Voici les transparents de l'exposé :

Désolé, pas de vidéo, ça n'a pas été filmé. Mais vous pouvez lire mes articles sur les RFC 7626 (description du problème), RFC 7858 (chiffrement du DNS) et RFC 7816 (minimisation des données envoyées). Et bien sûr l'excellent portail « DNS privacy ».

Merci beaucoup à Zenzla زنزلا pour la proposition et l'organisation.


L'article seul

RFC 8300: Network Service Header (NSH)

Date de publication du RFC : Janvier 2018
Auteur(s) du RFC : P. Quinn (Cisco), U. Elzur (Intel), C. Pignataro (Cisco)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF sfc
Première rédaction de cet article le 14 janvier 2018


Ce Network Service Header est un mécanisme concret pour faire passer sur le réseau les paquets destinés à une SF (Service Function, voir RFC 7665 pour l'architecture et les définitions). On colle un NSH, stockant plusieurs métadonnées, au paquet à traiter, on encapsule ce paquet à traiter et on l'envoie au dispositif de traitement via un réseau overlay. Et on fait l'opération inverse au retour. L'encapsulation peut se faire dans IP (par exemple avec GRE) ou dans un autre protocole.

Les métadonnées mises dans le NSH sont le résultat d'un processus de classification où le réseau décide ce qu'on va faire au paquet. Par exemple, en cas de dDoS, le classificateur décide de faire passer tous les paquets ayant telle adresse source par un équipement de filtrage plus fin, et met donc cette décision dans le NSH (section 7.1). Le NSH contient les informations nécessaires pour le SFC (Service Function Chain, RFC 7665). Sa lecture est donc très utile pour l'opérateur du réseau (elle contient la liste des traitements choisis, et cette liste peut permettre de déduire des informations sur le trafic en cours) et c'est donc une information plutôt sensible (voir aussi le RFC 8165).

Le NSH ne s'utilise qu'à l'intérieur de votre propre réseau (il n'offre, par défaut, aucune authentification et aucune confidentialité, voir section 8 du RFC). C'est à l'opérateur de prendre les mesures nécessaires, par exemple en chiffrant tout son trafic interne. Cette limitation à un seul domaine permet également de régler le problème de la fragmentation, si ennuyeux dès qu'on encapsule, ajoutant donc des octets. Au sein d'un même réseau, on peut contrôler tous les équipements et donc s'assurer que la MTU sera suffisante, ou, sinon, que la fragmentation se passera bien (section 5 du RFC).

Tout le projet SFC (dont c'est le troisième RFC) repose sur une vision de l'Internet comme étant un ensemble de middleboxes tripotant les paquets au passage, plutôt qu'étant un ensemble de machines terminales se parlant suivant le principe de bout en bout. C'est un point important à noter pour comprendre les débats au sein de l'IETF.


Téléchargez le RFC 8300


L'article seul

Cours BGP au CNAM

Première rédaction de cet article le 10 janvier 2018


Le 9 janvier 2018, c'était la première édition de mon cours BGP de trois heures au CNAM. Pour l'anecdote, c'était dans le bâtiment où il y avait eu la première connexion UUCP/Usenet, et le premier serveur HTTP public, en France.

Voici les supports de l'exposé :

Désolé, pas de vidéo publique.

J'ai fait aussi un cours DNS au même endroit.

Merci à Éric Gressier pour l'idée et l'organisation, et aux élèves pour avoir posé plein de questions pas toujours faciles.


L'article seul

RFC 8289: Controlled Delay Active Queue Management

Date de publication du RFC : Janvier 2018
Auteur(s) du RFC : K. Nichols (Pollere), V. Jacobson, A. McGregor, J. Iyengar (Google)
Expérimental
Réalisé dans le cadre du groupe de travail IETF aqm
Première rédaction de cet article le 6 janvier 2018


Ah, la gestion des files d'attentes… Le cauchemar de plein d'étudiants en informatique. Et cela ne cesse pas quand ils deviennent ingénieurs et qu'il faut construire un routeur pour connecter des réseaux de capacités différentes, et qui aura donc besoin de files d'attente. Bref, dès qu'on n'a pas assez de ressources (et on n'en aura jamais assez), il faut optimiser ses files d'attente. Ce nouveau RFC décrit le mécanisme CoDel (mis en œuvre depuis un certain temps dans le noyau Linux) qui permet notamment de limiter le terrible, l'épouvantable bufferbloat.

L'algorithme naïf pour gérer une file d'attente est le suivant (on prend le cas simple d'un routeur qui n'a que deux interfaces et une seule file d'attente dans chaque direction) : les paquets arrivent au routeur et sont mis dans la file gérée en FIFO. Dès que des ressources suffisantes sont disponibles pour envoyer un paquet (dès que l'interface de sortie est libre), on envoie le paquet et on le retire donc de la file. Si un paquet arrive quand la file est pleine, on le jette : TCP détectera cette perte, réduira son rythme d'envoi, et réémettra les données manquantes.

Avec cet algorithme simpliste, il y a une décision importante à prendre, la taille de la file. Le trafic sur l'Internet est tout sauf constant : des périodes de grand calme succèdent à des pics de trafic impressionnants. Si la file d'attente est trop petite, on ne pourra pas écluser ces pics, et on jettera des paquets. Ça tombe bien, le prix des mémoires baisse, on va pouvoir mettre des files plus grandes, non ? Eh bien non car arrive le fameux bufferbloat. Si la file est trop grande, les paquets y séjourneront très longtemps, et on augmentera ainsi la latence, au grand dam des applications sensibles comme SSH ou les jeux en ligne. Bref, on est coincés, il n'y a pas de solution idéale. Pire, si on ne jette des paquets que lorsque la file est pleine, on risque de tomber dans le cas où l'équilibre se fait avec une file d'attente toujours pleine, et donc avec une mauvaise latence.

Bien sûr, il y a longtemps que les routeurs n'utilisent plus d'algorithme aussi naïf que celui présenté ici. Tout un travail a été fait sur l'AQM : voir par exemple les RFC 7567 et RFC 8033. Mais le problème de la file d'attente toujours pleine est toujours là. Sa première description est dans le RFC 896 en 1984. Plus récemment, on le trouve mentionné dans une présentation de Jim Gettys à l'IETF 80 (« Bufferbloat: Dark Buffers in the Internet ») développé dans un article dans Communications of the ACM (Gettys a beaucoup fait pour la prise de conscience des dangers du bufferbloat.).

Résoudre le problème de l'« obésité du tampon » (bufferbloat) en réduisant la taille des files d'attentes ne serait pas une solution : les tampons sont là pour une bonne raison, pour absorber les problèmes brefs, lorsque, temporairement, on reçoit davantage de paquets que ce que l'on peut transmettre. Le fait que des files plus petites ne sont pas une solution a déjà été exposé dans le RFC 2309, dans « A Rant on Queues » de Van Jacobson, dans le rapport « RED in a Different Light » et dans l'article fondateur de CoDel, « Controlling Queue Delay » (article complet sur Sci-Hub, cherchez 10.1145/2209249.2209264). Le problème n'est pas tant la taille de la file en soi (ce n'est pas la taille qui compte), mais si c'est une « bonne » file ou une « mauvaise » file (au passage, si vous ne connaissez pas la différence entre le bon chasseur et le mauvais chasseur, ne ratez pas l'indispensable sketch des Inconnus).

CoDel est donc une vieille idée. Elle veut répondre, entre autres, aux critères suivants (section 1 du RFC) :

  • Être « zéro-configuration » (parameterless), ce qui avait été un problème fréquent de solutions comme RED. (Mon opinion personnelle est que CoDel n'est pas réellement sans configuration, comme on le voit plus loin dans le RFC, mais il est certainement « configuration minimale ».) CoDel s'ajuste tout seul, une fois défini l'ordre de grandeur du RTT des paquets qui passeront par le routeur.
  • Capable de différencier le mauvais chasseur du bon chasseur, euh, pardon, la mauvaise file d'attente de la bonne.
  • Être simple à programmer, pour fonctionner aussi bien dans les processeurs limités des routeurs low cost que dans les ASIC (rapides, mais pas très souples) des routeurs haut de gamme.

Lorsque CoDel estime nécessaire de prendre des mesures (le trafic entre trop vite), il peut jeter les paquets, ou les marquer avec ECN (RFC 3168).

La section 2 de notre RFC définit la terminologie de CoDel. Parmi les termes importants :

  • Temps de passage (sojourn time) : le temps passé par le paquet dans la file d'attente. C'est la donnée de base de CoDel, qui va essayer de minimiser ce temps de passage.
  • File persistante (standing queue) : une file d'attente qui reste pleine trop longtemps, « trop » étant de l'ordre du RTT le plus élevé parmi les flots des paquets qui attendent dans la file.

Passons maintenant à une description de haut niveau de CoDel. Son but est de différencier la mauvaise file (qui ne fait qu'ajouter du retard d'acheminement des paquets) de la bonne. Une file d'attente se forme lorsqu'il y a un goulet d'étranglement, parce qu'un lien à forte capacité se déverse dans un lien à faible capacité, ou bien parce que plusieurs liens convergent vers un lien ayant la capacité de seulement l'un d'eux. Une notion importante à ce sujet est celle de BDP, en gros le nombre d'octets en transit pour une connexion donnée, lorsque le débit atteint 100 % de la capacité. Imaginons une connexion TCP dont la fenêtre d'envoi est de 25 paquets (je sais bien que les fenêtres TCP se calculent en octets, pas en paquets, mais c'est le RFC qui fait cet abus de langage, pas moi) et où le BDP est de 20 paquets. En régime permanent, il y aura 5 paquets dans la file d'attente. Si la fenêtre est de 30 paquets, ce seront 10 paquets qui occuperont en permanence la file d'attente, augmentant encore le délai, alors que le débit ne changera pas (20 paquets arriveront par RTT). Au contraire, si on réduit la fenêtre à 20 paquets, la file d'attente sera vide, le délai sera réduit au minimum, alors que le débit n'aura pas changé. Ce résultat contre-intuitif montre que bourrer la connexion de paquets n'est pas forcément le meilleur moyen d'aller « vite ». Et que la taille de la file ne renseigne pas sur le rythme d'envoi des données. Et enfin que les files pleines en permanence n'apportent que du délai. Dans le premier exemple, la file contenant cinq paquets tout le temps était clairement une mauvaise file. Un tampon d'entrée/sortie de 20 paquets est pourtant une bonne chose (pour absorber les variations brutales) mais, s'il ne se vide pas complètement ou presque en un RTT, c'est qu'il est mal utilisé. Répétons : Les bonnes files se vident vite.

CoDel comporte trois composants : un estimateur, un objectif et une boucle de rétroaction. La section 3 de notre RFC va les présenter successivement. Pour citer l'exposé de Van Jacobson à une réunion IETF, ces trois composants sont :

  • a) Measure what you’ve got
  • b) Decide what you want
  • c) If (a) isn’t (b), move it toward (b)

D'abord, l'estimateur. C'est la partie de CoDel qui observe la file d'attente et en déduit si elle est bonne ou mauvaise. Autrefois, la principale métrique était la taille de la file d'attente. Mais celle-ci peut varier très vite, le trafic Internet étant très irrégulier. CoDel préfère donc observer le temps de séjour dans la file d'attente. C'est une information d'autant plus essentielle qu'elle a un impact direct sur le vécu de l'utilisateur, via l'augmentation de la latence.

Bon, et une fois qu'on observe cette durée de séjour, comment en déduit-on que la file est bonne ou mauvaise ? Notre RFC recommande de considérer la durée de séjour minimale. Si elle est nulle (c'est-à-dire si au moins un paquet n'a pas attendu du tout dans la file, pendant la dernière période d'observation), alors il n'y a pas de file d'attente permanente, et la file est bonne.

Le RFC parle de « période d'observation ». Quelle doit être la longueur de cette période ? Au moins un RTT pour être sûr d'écluser les pics de trafic, mais pas moins pour être sûr de détecter rapidement les mauvaises files. Le RFC recommande donc de prendre comme longueur le RTT maximal de toutes les connexions qui empruntent ce tampon d'entrée/sortie.

Et, petite astuce d'implémentation (un routeur doit aller vite, et utilise souvent des circuits de calcul plus simples qu'un processeur généraliste), on peut calculer la durée de séjour minimale avec une seule variable : le temps écoulé depuis lequel la durée de séjour est inférieure ou supérieure au seuil choisi. (Dans le pseudo-code de la section 5, et dans le noyau Linux, c'est à peu près le rôle de first_above_time.)

Si vous aimez les explications avec images, il y en a plein dans cet excellent exposé à l'IETF.

Ensuite, l'objectif (appelé également référence) : il s'agit de fixer un objectif de durée de séjour dans la file. Apparemment, zéro serait l'idéal. Mais cela entrainerait des « sur-réactions », où on jetterait des paquets (et ralentirait TCP) dès qu'une file d'attente se forme. On va plutôt utiliser un concept dû à l'inventeur du datagramme, Leonard Kleinrock, dans « An Invariant Property of Computer Network Power », la « puissance » (power). En gros, c'est le débit divisé par le délai et l'endroit idéal, sur la courbe de puissance, est en haut (le maximum de débit, pour le minimum de délai). Après une analyse que je vous épargne, le RFC recommande de se fixer comme objectif entre 5 et 10 % du RTT.

Enfin, la boucle de rétroaction. Ce n'est pas tout d'observer, il faut agir. CoDel s'appuie sur la théorie du contrôle, pour un système MIMO (Multi-Input Multi-Output). Au passage, si vous ne comprenez rien à la théorie du contrôle et notamment à la régulation PID, je vous recommande chaudement un article qui l'explique sans mathématiques. Placé à la fin de la file d'attente, au moment où on décide quoi faire des paquets, le contrôleur va les jeter (ou les marquer avec ECN) si l'objectif de durée de séjour est dépassé.

Passons maintenant à la section 4 du RFC, la plus concrète, puisqu'elle décrit précisement CoDel. L'algorithme a deux variables, TARGET et INTERVAL (ces noms sont utilisés tels quels dans le pseudo-code en section 5, et dans l'implémentation dans le noyau Linux). TARGET est l'objectif (le temps de séjour dans la file d'attente qu'on ne souhaite pas dépasser) et INTERVAL est la période d'observation. Ce dernier est également le seul paramètre de CoDel qu'il faut définir explicitement. Le RFC recommande 100 ms par défaut, ce qui couvre la plupart des RTT qu'on rencontre dans l'Internet, sauf si on parle à des gens très lointains ou si on passe par des satellites (cf. M. Dischinger, « Characterizing Residential Broadband Networks », dans les Proceedings of the Internet Measurement Conference, San Diego, en 2007, si vous voulez des mesures). Cela permet, par exemple, de vendre des routeurs bas de gamme, genre point d'accès Wifi sans imposer aux acheteurs de configurer CoDel.

Si on est dans un environnement très différent de celui d'un accès Internet « normal », il peut être nécessaire d'ajuster les valeurs (CoDel n'est donc pas réellement « parameterless »). L'annexe A du RFC en donne un exemple, pour le cas du centre de données, où les latences sont bien plus faibles (et les capacités plus grandes) ; INTERVAL peut alors être défini en microsecondes plutôt qu'en millisecondes.

TARGET, lui, va être déterminé dynamiquement par CoDel. Une valeur typique sera aux alentours de 5 ms (elle dépend évidemment de INTERVAL) : si, pendant une durée égale à INTERVAL, les paquets restent plus longtemps que cela dans la file d'attente, c'est que c'est une mauvaise file, on jette des paquets. Au passage, dans le noyau Linux, c'est dans codel_params_init que ces valeurs sont fixées :

 	params->interval = MS2TIME(100);
	params->target = MS2TIME(5);
   

Les programmeurs apprécieront la section 5, qui contient du pseudo-code, style C++, mettant en œuvre CoDel. Le choix de C++ est pour profiter de l'héritage, car CoDel est juste une dérivation d'un classique algorithme tail-drop. On peut donc le programmer sous forme d'une classe qui hérite d'une classe queue_t, plus générale.

De toute façon, si vous n'aimez pas C++, vous pouvez lire le code source du noyau Linux, qui met en œuvre CoDel depuis longtemps (fichier net/sched/sch_codel.c, également accessible via le Web).

(Pour comprendre les exemples de code, notez que packet_t* pointe vers un descripteur de paquet, incluant entre autres un champ, tstamp, qui stocke un temps, et que le type time_t est exprimé en unités qui dépendent de la résolution du système, sachant que la milliseconde est suffisante, pour du trafic Internet « habituel ».) Presque tout le travail de CoDel est fait au moment où le paquet sort de la file d'attente. À l'entrée, on se contente d'ajouter l'heure d'arrivée, puis on appelle le traitement habituel des files d'attente :

void codel_queue_t::enqueue(packet_t* pkt)
   {
       pkt->tstamp = clock();
       queue_t::enqueue(pkt);
   }       
     

Le gros du code est dans le sous-programme dodequeue qui va déterminer si le paquet individuel vient de nous faire changer d'état :


dodequeue_result codel_queue_t::dodequeue(time_t now)
   {
   ...
       // Calcul de *la* variable importante, le temps passé dans la
       // file d'attente
       time_t sojourn_time = now - r.p->tstamp;
       // S'il est inférieur à l'objectif, c'est bon
       if (sojourn_time_ < TARGET || bytes() <= maxpacket_) {
           first_above_time_ = 0;
       } else {
           // Aïe, le paquet est resté trop longtemps (par rapport à TARGET)
           if (first_above_time_ == 0) {
	       // Pas de panique, c'est peut-être récent, attendons
	       // INTERVAL avant de décider
               first_above_time_ = now + INTERVAL;
           } else if (now >= first_above_time_) {
               // La file ne se vide pas : jetons le paquet
               r.ok_to_drop = true;
           }
       }
       return r;
   }

     

Le résultat de dodequeue est ensuite utilisé par dequeue qui fait se fait quelques réflexions supplémentaires avant de jeter réellement le paquet.

Ce code est suffisamment simple pour pouvoir être mis en œuvre dans le matériel, par exemple ASIC ou NPU.

L'annexe A du RFC donne un exemple de déclinaison de CoDel pour le cas d'un centre de données, où les RTT typiques sont bien plus bas que sur l'Internet public. Cette annexe étudie également le cas où on applique CoDel aux files d'attente des serveurs, pas uniquement des routeurs, avec des résultats spectaculaires :

  • Dans le serveur, au lieu de jeter le paquet bêtement, on prévient directement TCP qu'il doit diminuer la fenêtre,
  • Le RTT est connu du serveur, et peut donc être mesuré au lieu d'être estimé (on en a besoin pour calculer INTERVAL),
  • Les paquets de pur contrôle (ACK, sans données) ne sont jamais jetés, car ils sont importants (et de petite taille, de toute façon).

Sur l'Internet public, il serait dangereux de ne jamais jeter les paquets de pur contrôle, ils pourraient être envoyés par un attaquant. Mais, dans le serveur, aucun risque.


Téléchargez le RFC 8289


L'article seul

RFC 8307: Well-Known URIs for the WebSocket Protocol

Date de publication du RFC : Janvier 2018
Auteur(s) du RFC : C. Bormann (Universitaet Bremen TZI)
Chemin des normes
Première rédaction de cet article le 3 janvier 2018


Il existe une norme pour un préfixe de chemin dans un URI, préfixe nommée .well-known, et après lequel plusieurs noms sont normalisés, pour des ressources « bien connues », c'est-à-dire auxquelles on peut accéder sans lien qui y mène. Le RFC 5785 normalise ce .well-known. Il n'était prévu à l'origine que pour les plans http: et https:. Ce très court RFC l'étend aux plans ws: et wss:, ceux des Web sockets du RFC 6455.

Les gens de CoAP avaient déjà étendu l'usage de .well-known en permettant (RFC 7252) qu'il soit utilisé pour les plans coap: et coaps:.

Il existe un registre IANA des suffixes (les termes après .well-known). Ce registre est le même quel que soit le plan d'URI utilisé. Il ne change donc pas suite à la publication de ce RFC.


Téléchargez le RFC 8307


L'article seul

Fiche de lecture : Le médecin qui voulut être roi

Auteur(s) du livre : Guillaume Lachenal
Éditeur : Seuil
9-782021-142563
Publié en 2017
Première rédaction de cet article le 3 janvier 2018


Voici un excellent livre sur le colonialisme, à travers le cas d'un médecin français, Jean-Joseph David, qui, en poste à Wallis, puis au Cameroun, a obtenu les pleins pouvoirs, et s'est lancé dans une utopie, créer une société parfaite.

C'est une banalité de parler de pouvoir médical et de dire que le médecin se sent souvent placé au dessus des autres humains. Mais ici, dans les deux pays, David a en effet réussi à avoir la totalité des pouvoirs, médical, mais aussi économique, administratif, policier et judiciaire (il pouvait punir, et il ne s'en privait pas). Tout partait pourtant d'une idée sympathique, celle de la « médecine sociale », l'idée, en rupture avec la médecine pasteurienne, qu'on ne pouvait pas considérer la maladie comme le résultat de la seule action des microbes, mais qu'il fallait prendre en compte tout l'environnement. Mais cette idée a vite mené à un dérapage qu'on doit pouvoir qualifier de totalitaire. Le médecin s'est en effet senti en droit de réorganiser toute la société selon ses vues, évidemment sans jamais demander leur avis aux habitants.

Jean-Joseph David était-il gentil ou méchant ? Il traitait bien les indigènes, mais comme un éleveur traite bien son troupeau. Il ne les considérait pas comme des citoyens, même pas complètement comme des humains, quand il parlait d'« améliorer la race » ou bien d'amener beaucoup d'enfants à l'âge adulte pour que la France ait beaucoup de tirailleurs. Et ses utopies ont parfois eu des conséquences graves. Il était clairement mégalomane (« la mégalomanie est une maladie tropicale », note un ancien des interventions en Afrique à l'auteur) mais était aussi le produit de son époque (la culture du Pharo, où tous ces médecins coloniaux étaient formés par d'anciens coloniaux) et de l'éloignement (depuis Yaoundé, le gouverneur français le laissait faire ce qu'il voulait).

Le titre du livre fait évidemment référence à Kipling mais, ici, le héros ne cherche pas juste à être roi et à en avoir les avantages, il veut vraiment transformer la société.

Le livre est un peu décousu, d'abord parce qu'on a peu d'archives sur David (une grande partie a disparu dans les troubles qu'a connu le Cameroun par la suite), ensuite parce que ce n'est pas un roman, mais un travail plutôt universitaire, avec beaucoup de notions de sociologie et d'anthropologie qui nécessiteront que l·e·a lect·eur·rice s'accroche. Mais cela vaut la peine, il y a eu peu de descriptions de ce genre d'« utopies coloniales » où un mégalomane, profitant du pouvoir que lui donnait la force militaire de la France, se disait « je vais enfin pouvoir remodeler la société ». On est choqués de sa dictature et en même temps, c'est fascinant de se demander « et moi, si j'avais un tel pouvoir, j'en ferais quoi ? »


L'article seul

Fiche de lecture : La mer des Cosmonautes

Auteur(s) du livre : Cédric Gras
Éditeur : Paulsen
Publié en 2017
Première rédaction de cet article le 1 janvier 2018


Il fait froid dehors, en ce début 2018 ? Voici un livre pour se réchauffer : partez en Antarctique avec les polyarniks russes.

Ce n'est pas un roman, mais un témoignage de l'auteur, qui est allé en Antarctique (en été, quand même…) à bord du Akademik Fedorov et en a rapporté plein d'histoires de travailleurs polaires. Et, oui, la mer des Cosmonautes du titre existe vraiment.

Le temps des explorateurs polaires avec trois types et quinze chiens qui se lançaient à l'assaut du continent blanc est passé : les bases sont maintenant bien plus grandes et peuplées, avec une débauche de matériel. Dans les bases russes, on trouve tous les fleurons de l'ex-industrie soviétique, devenues souvent épaves, de l'Iliouchine 14 au GAZ-69. On est maintenant dans l'exploitation plus que l'exploration. Mais les conditions de vie restent difficiles : le matériel casse, les humains s'épuisent, l'accident guette dès qu'on sort, il faut parfois allumer un feu sous le bulldozer pour qu'il démarre. Et les Russes prennent apparemment moins de précautions avec le matériel humain, alimentant le cimetière qui jouxte une des bases.

Mais pourquoi trouve-t-on des volontaires pour l'Antarctique ? À l'époque soviétique, une des raisons était le prestige attaché au métier de polyarnik. Leur prestige ne le cédait qu'aux cosmonautes, héros souvent cités par les polyarniks nostalgiques « à cette époque, on était quasiment aussi adulés que les cosmonautes », et les gamins dans les cours d'école jouaient en alternance au cosmonaute et au travailleur polaire. Tout ceci s'est effondré avec l'Union Soviétique, au point que, dans certaines bases, il a fallu vendre les breloques à étoile rouge aux touristes états-uniens de la base voisine, pour pouvoir se financer. Il n'y a plus qu'en Biélorussie, gelée dans les années Brejnev, que les polyarniks sont encore accueillis avec médailles et discours, à leur retour.

Aujourd'hui, la Russie continue l'exploitation antarctique, mais avec un nombre de bases réduit. Des nouveaux pays sont venus, et on s'échange les visites de courtoisie, dans ce territoire démilitarisé. Le travailleur polaire continue de bénéficier d'avantages matériels conséquents, et garde un peu de prestige, pas à cause de l'Antarctique, mais à cause des escales exotiques que fait le navire avant d'atteindre le continent blanc. Et puis, certains travailleurs le confient à l'auteur : après plusieurs rotations en Antarctique, ils ne se voient pas vraiment reprendre une vie « normale ».

En ligne, les photos de l'auteur.


L'article seul

Géolocation d'une adresse IP via le DNS

Première rédaction de cet article le 24 décembre 2017


Pour Noël, un truc complètement inutile mais amusant : trouver la longitude et la latitude d'une adresse IP via le DNS.

Ce service est fourni par Bert Hubert, l'auteur de PowerDNS, via le domaine geo.lua.powerdns.org. (Il ne marche apparemment que pour IPv4.) On inverse l'adresse IP (comme pour in-addr.arpa) et on fait une requête pour le type TXT. Exemple avec l'adresse du serveur Web de l'AFNIC, 192.134.5.24 :

      
% dig TXT 24.5.134.192.geo.lua.powerdns.org
...;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 40951
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 2, ADDITIONAL: 1
...
;; ANSWER SECTION:
24.5.134.192.geo.lua.powerdns.org. 3600	IN TXT "48.858200 2.338700"

    

On peut automatiser l'opération avec awk (merci à Oli Schacher‏) :

% reverse() { echo $1  | awk -F. '{print $4"."$3"." $2"."$1}' }

% reverse 192.134.5.24
24.5.134.192
    

Une fois que c'est fait, on peut lancer un navigateur Web directement vers la carte, ici avec OpenStreetMap :


% show-ip() { x-www-browser https://www.openstreetmap.org/\?$(dig  $(echo $1 | awk -F. '{print $4"."$3"." $2"."$1}').geo.lua.powerdns.org TXT +short | head -1 | awk '{gsub(/"/, ""); print "mlat="$1"&mlon="$2"&zoom=12"}')}         

% show-ip 192.134.5.24

    

Et si vous préférez Evil Corp. :


% show-ip() { x-www-browser https://maps.google.com/maps\?q=$(dig  $(echo $1 | awk -F. '{print $4"."$3"." $2"."$1}').geo.lua.powerdns.org TXT +short | awk '{gsub(/"/, ""); print $1","$2}')}

    

La base de géolocalisation utilisée est celle de MaxMind qui, comme toutes les bases de géolocalisation vaut ce qu'elle vaut (le serveur Web de l'AFNIC n'est pas au centre de Paris…)


L'article seul

RFC 8305: Happy Eyeballs Version 2: Better Connectivity Using Concurrency

Date de publication du RFC : Décembre 2017
Auteur(s) du RFC : D. Schinazi, T. Pauly (Apple)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF v6ops
Première rédaction de cet article le 21 décembre 2017


Une machine connectée à l'Internet et répondant aux requêtes venues du réseau a souvent plusieurs adresses IP pour son nom. C'est parfois une adresse IPv4 et une IPv6 mais cela peut aussi être plusieurs adresses IPv6, ou bien un mélange en proportions quelconques. Les développeurs d'application et les administrateurs système qui déploieront ces applications ensuite, ont un choix difficile si certaines de ces adresses marchent et d'autres pas (ou mal). Si les différentes adresses IP de cette machine passent par des chemins différents, certains marchant et d'autres pas, l'application arrivera-t-elle à se rabattre sur une autre adresse très vite ou bien imposera-t-elle à l'utilisateur un long délai avant de détecter enfin le problème ? Cette question est connue comme « le bonheur des globes oculaires » (les dits globes étant les yeux de l'utilisateur qui attend avec impatience la page d'accueil de PornHub) et ce RFC spécifie les exigences pour l'algorithme de connexion du client. En les suivant, les globes oculaires seront heureux. Il s'agit de la version 2 de l'algorithme, bien plus élaborée que la version 1 qui figurait dans le RFC 6555.

La section 1 rappelle les données du problème : on veut évidemment que cela marche aussi bien en IPv6 (RFC 8200) qu'en IPv4 (pas question d'accepter des performances inférieures) or, dans l'état actuel du déploiement d'IPv6, bien des sites ont une connexion IPv6 totalement ou partiellement cassée. Si un serveur a IPv4 et IPv6 et que son client n'a qu'IPv4, pas de problème. Mais si le client a IPv6, tente de l'utiliser, mais que sa connexion est plus ou moins en panne, ou simplement sous-optimale, ses globes oculaires vont souffrir d'impatience. On peut aussi noter que le problème n'est pas spécifique à IPv6 : du moment que la machine visée a plusieurs adresses, qu'elles soient IPv4 ou IPv6, le risque que certaines des adresses ne marchent pas (ou moins bien) existe, et l'algorithme des globes oculaires heureux doit être utilisé. (C'est un des gros changements avec le précédent RFC, le RFC 6555, qui n'envisageait que le cas IPv6.)

La bonne solution est donc que l'application elle-même gère le problème (ou, sinon l'application elle-même, la bibliothèque logicielle qu'elle utilise et où se trouve la fonction de connexion). Il existe plusieurs algorithmes pour cela, déjà largement déployés depuis des années. On peut donc se baser sur l'expérience pour spécifier ces algorithmes. Ce RFC normalise les caractéristiques que doivent avoir ces algorithmes. Si on suit ce RFC, le trafic (IP et DNS) va légèrement augmenter (surtout si la connectivité IPv6 marche mal ou pas du tout) mais la qualité du vécu de l'utilisateur va être maintenue, même en présence de problèmes, ce qui compense largement. Autrement, il existerait un risque élevé que certains utilisateurs coupent complètement IPv6, plutôt que de supporter ces problèmes de délai de connexion.

La cible principale de notre RFC est composée des protocoles de transport avec connexion (TCP, SCTP), les protocoles sans connexion comme UDP soulevant d'autres questions (s'ils ont une sémantique requête/réponse, comme dans ICE, les algorithmes de ce RFC peuvent être utilisés).

Donc, on a un nom de machine qu'on veut contacter, mettons www.example.com, avec plusieurs adresses associées, peut-être de familles (v4 et v6) différentes. Prenons une machine ayant une seule adresse IPv4 et une seule adresse IPv6, avec une connexion IPv6 qui marche mal. Avec l'algorithme naïf qu'utilisent encore certains logiciels voici la séquence d'évenements traditionnelle :

  • L'initiateur de la connexion utilise le DNS pour demander les enregistrements A (adresse IPv4) et AAAA (IPv6).
  • Il récupère 192.0.2.1 et 2001:db8::1.
  • Il tente IPv6 (sur Linux, l'ordre des essais est réglable dans /etc/gai.conf). L'initiateur envoie un paquet TCP SYN à 2001:db8::1.
  • Pas de réponse (connexion IPv6 incorrecte). L'initiateur réessaie, deux fois, trois fois, faisant ainsi perdre de nombreuses secondes.
  • L'initiateur renonce, il passe à IPv4 et envoie un paquet TCP SYN à 192.0.2.1.
  • Le répondeur envoie un SYN+ACK en échange, l'initiateur réplique par un ACK et la connexion TCP est établie.

Le problème de cet algorithme naïf est donc la longue attente lors des essais IPv6. On veut au contraire un algorithme qui bascule rapidement en IPv4 lorsqu'IPv6 ne marche pas, sans pour autant gaspiller les ressources réseau en essayant par exemple toutes les adresses en même temps.

L'algorithme recommandé (sections 3 à 5, cœur de ce RFC) aura donc l'allure suivante :

  • L'initiateur de la connexion utilise le DNS pour demander les enregistrements A (adresse IPv4) et AAAA (IPv6).
  • Il récupère 192.0.2.1 et 2001:db8::1. Il sait donc qu'il a plusieurs adresses, de famille différente.
  • Il tente IPv6 (l'algorithme du RFC est de toute façon facilement adaptable à des cas où IPv4 est prioritaire). L'initiateur envoie un paquet TCP SYN à 2001:db8::1, avec un très court délai de garde.
  • Pas de réponse quasi-immédiate ? L'initiateur passe à IPv4 rapidement. Il envoie un paquet TCP SYN à 192.0.2.1.
  • Le répondeur envoie un SYN+ACK en échange, l'initiateur réplique par un ACK et la connexion TCP est établie.

Si le répondeur réagit à une vitesse normale en IPv6, la connexion sera établie en IPv6. Sinon, on passera vite en IPv4, et l'utilisateur humain ne s'apercevra de rien. Naturellement, si le DNS n'avait rapporté qu'une seule adresse (v4 ou v6), on reste à l'algorithme traditionnel (« essayer, patienter, ré-essayer »).

Maintenant, les détails. D'abord, le DNS (section 3 de notre RFC). Pour récupérer les adresses appartenant aux deux familles (IPv4 et IPv6), il faut envoyer deux requêtes, de type A et AAAA. Pas de délai entre les deux, et le AAAA en premier, recommande le RFC. Notez qu'il n'existe pas de type de requête DNS pour avoir les deux enregistrements d'un coup, il faut donc deux requêtes.

Il ne faut pas attendre d'avoir la réponse aux deux avant de commencer à tenter d'établir une connexion. En effet, certains pare-feux configurés avec les pieds bloquent les requêtes AAAA, qui vont finir par timeouter. Du point de vue du programmeur, cela signifie qu'il faut faire les deux requêtes DNS dans des fils différents (ou des goroutines différentes en Go), ou bien, utiliser une API asynchrone, comme getdns. Ensuite, si on reçoit la réponse AAAA mais pas encore de A, on essaye tout de suite de se connecter, si on a la réponse A, on attend quelques millisecondes la réponse AAAA puis, si elle ne vient pas, tant pis, on essaie en IPv4. (La durée exacte de cette attente est un des paramètres réglables de l'algorithme. Il se nomme Resolution Delay et sa valeur par défaut recommandée est de 50 ms.)

À propos de DNS, notez que le RFC recommande également de privilégier IPv6 pour le transport des requêtes DNS vers les résolveurs (on parle bien du transport des paquets DNS, pas du type des données demandées). Ceci dit, ce n'est pas forcément sous le contrôle de l'application.

Une fois récupérées les adresses, on va devoir les trier selon l'ordre de préférence. La section 4 décrit comment cela se passe. Rappelons qu'il peut y avoir plusieurs adresses de chaque famille, pas uniquement une v4 et une v6, et qu'il est donc important de gérer une liste de toutes les adresses reçues (imaginons qu'on ne récupère que deux adresses v4 et aucune v6 : l'algorithme des globes oculaires heureux est quand même crucial car il est parfaitement possible qu'une des adresses v4 ne marche pas).

Pour trier, le RFC recommande de suivre les règles du RFC 6724, section 6. Si le client a un état (une mémoire des connexions précédentes, ce qui est souvent le cas chez les clients qui restent longtemps à tourner, un navigateur Web, par exemple), il peut ajouter dans les critères de tri le souvenir des succès (ou échecs) précédents, ainsi que celui des RTT passés. Bien sûr, un changement de connectivité (détecté par le DNA des RFC 4436 ou RFC 6059) doit entraîner un vidage complet de l'état (on doit oublier ce qu'on a appris, qui n'est plus pertinent).

Dernier détail sur le tri : il faut mêler les adresses des deux familles. Imaginons un client qui récupère trois adresses v6 et trois v4, client qui donne la priorité à IPv4, mais dont la connexion IPv4 est défaillante. Si sa liste d'adresses à tester comprend les trois adresses v4 en premier, il devra attendre trois essais avant que cela ne marche. Il faut donc plutôt créer une liste {une adressse v4, une adresse v6, une adresse v4…}. Le nombre d'adresses d'une famille à inclure avant de commencer l'autre famille est le paramètre First Address Family Count, et il vaut un par défaut.

Enfin, on essaie de se connecter en envoyant des paquets TCP SYN (section 5). Il est important de ne pas tester IPv4 tout de suite. Les premiers algorithmes « bonheur des globes oculaires » envoyaient les deux paquets SYN en même temps, gaspillant des ressources réseau et serveur. Ce double essai faisait que les équipements IPv4 du réseau avaient autant de travail qu'avant, alors qu'on aurait souhaité les retirer du service petit à petit. En outre, ce test simultané fait que, dans la moitié des cas, la connexion sera établie en IPv4, empêchant de tirer profit des avantages d'IPv6 (cf. RFC 6269). Donc, on doit tester en IPv6 d'abord, sauf si on se souvient des tentatives précédentes (voir plus loin la variante « avec état ») ou bien si l'administrateur système a délibérement configuré la machine pour préférer IPv4.

Après chaque essai, on attend pendant une durée paramétrable, Connection Attempt Delay, 250 ms par défaut (bornée par les paramètres Minimum Connection Attempt Delay, 100 ms par défaut, qu'on ne devrait jamais descendre en dessous de 10 ms, et Maximum Connection Attempt Delay, 2 s par défaut).

L'avantage de cet algorithme « IPv6 d'abord puis rapidement basculer en IPv4 » est qu'il est sans état : l'initiateur n'a pas à garder en mémoire les caractéristiques de tous ses correspondants. Mais son inconvénient est qu'on recommence le test à chaque connexion. Il existe donc un algorithme avec état (cf. plus haut), où l'initiateur peut garder en mémoire le fait qu'une machine (ou bien un préfixe entier) a une adresse IPv6 mais ne répond pas aux demandes de connexion de cette famille. Le RFC recommande toutefois de re-essayer IPv6 au moins toutes les dix minutes, pour voir si la situation a changé.

Une conséquence de l'algorithme recommandé est que, dans certains cas, les deux connexions TCP (v4 et v6) seront établies (si le SYN IPv6 voyage lentement et que la réponse arrive après que l'initiateur de la connexion se soit impatienté et soit passé à IPv4). Cela peut être intéressant dans certains cas rares, mais le RFC recommande plutôt d'abandonner la connexion perdante (la deuxième). Autrement, cela pourrait entraîner des problèmes avec, par exemple, les sites Web qui lient un cookie à l'adresse IP du client, et seraient surpris de voir deux connexions avec des adresses différentes.

La section 9 du RFC rassemble quelques derniers problèmes pratiques. Par exemple, notre algorithme des globes oculaires heureux ne prend en compte que l'établissement de la connexion. Si une adresse ne marche pas du tout, il choisira rapidement la bonne. Mais si une adresse a des problèmes de MTU et pas l'autre, l'établissement de la connexion, qui ne fait appel qu'aux petits paquets TCP SYN, se passera bien alors que le reste de l'échange sera bloqué. Une solution possible est d'utiliser l'algorithme du RFC 4821.

D'autre part, l'algorithme ne tient compte que de la possibilité d'établir une connexion TCP, ce qui se fait typiquement uniquement dans le noyau du système d'exploitation du serveur. L'algorithme ne garantit pas qu'une application écoute, et fonctionne.

Parmi les problèmes résiduels, notez que l'algorithme des globes oculaires heureux est astucieux, mais tend à masquer les problèmes (section 9.3). Si un site Web publie les deux adresses mais que sa connectivité IPv6 est défaillante, aucun utilisateur ne lui signalera puisque, pour eux, tout va bien. Il est donc recommandé que l'opérateur fasse des tests de son côté pour repérer les problèmes (le RFC 6555 recommandait que le logiciel permette de débrayer cet algorithme, afin de tester la connectivité avec seulement v4 ou seulement v6, ou bien que le logiciel indique quelque part ce qu'il a choisi, pour mieux identifier d'éventuels problèmes v6.)

Pour le délai entre le premier SYN IPv6 et le premier SYN IPv4, la section 5 donne des idées quantitatives en suggérant 250 ms entre deux essais. C'est conçu pour être quasiment imperceptible à un utilisateur humain devant son navigateur Web, tout en évitant de surcharger le réseau inutilement. Les algorithmes avec état ont le droit d'être plus impatients, puisqu'ils peuvent se souvenir des durées d'établissement de connexion précédents.

Notez que les différents paramètres réglables indiqués ont des valeurs par défaut, décrites en section 8, et qui ont été déterminées empiriquement.

Si vous voulez une meilleure explication de la version 2 des globes oculaires heureux, il y a cet exposé au RIPE.

Enfin, les implémentations. Notez que les vieilles mises en œuvre du RFC 6555 (et présentées à la fin de mon précédent article) sont toujours conformes à ce nouvel algorithme, elles n'en utilisent simplement pas les raffinements. Les versions récentes de macOS (Sierra) et iOS (10) mettent en œuvre notre RFC, ce qui est logique, puisqu'il a été écrit par des gens d'Apple (l'annonce est ici, portant même sur des versions antérieures). Apple en a d'ailleurs profité pour breveter cette technologie. À l'inverse, un exemple récent de logiciel incapable de gérer proprement le cas d'un pair ayant plusieurs adresses IP est Mastodon (cf. bogue #3762.)

Dans l'annexe A, vous trouverez la liste complète des importants changements depuis le RFC 6555. Le précédent RFC n'envisageait qu'un seul cas, deux adresses IP, une en v4, l'autre en v6. Notre nouveau RFC 8305 est plus riche, augmente le parallélisme, et ajoute :

  • La façon de faire les requêtes DNS (pour tenir compte des serveurs bogués qui ne répondent pas aux requêtes AAAA, cf. RFC 4074),
  • La gestion du cas où il y a plusieurs adresses IP de la même famille (v4 ou v6),
  • La bonne façon d'utiliser les souvenirs des connexions précédentes,
  • Et la méthode (dont je n'ai pas parlé ici) pour le cas des réseaux purement IPv6, mais utilisant le NAT64 du RFC 8305.

Téléchargez le RFC 8305


L'article seul

Articles des différentes années : 2018  2017  2016  2015  2014  2013  2012  Précédentes années

Syndication : en HTTP non sécurisé, Flux Atom avec seulement les résumés et Flux Atom avec tout le contenu, en HTTPS, Flux Atom avec seulement les résumés et Flux Atom avec tout le contenu.