<?xml version="1.0" encoding="utf-8"?>
<rfcdesc title="Configuring UDP Sockets for Explicit Congestion Notification (ECN) for Common Platforms" num="10055" wg="tsvwg" status="informational">
  <authors><author>M. Duke (Google)</author></authors>
  <rfcdate><month>October</month><year>2026</year></rfcdate>
  <date>2026-10-16</date>
<content>
  <p>Un <wikipedia name="Request for comments">RFC</wikipedia> un peu
  inhabituel que voici. Il ne spécifie pas un protocole ou un format,
  il explique simplement comment activer la notification de congestion
  (ECN) sur les <wikipedia name="Système d'exploitation">systèmes
  d'exploitation</wikipedia> courants.</p>
  <p>Cette notification (ECN = <foreign>Explicit Congestion
  Notification</foreign>) est normalisée dans le <rfc num="3168"
  local="true"/> et permet de signaler en aval de la machine qui met
  l'ECN qu'elle voit de la congestion. Elle est surtout utilisée avec
  <wikipedia name="Transmission Control Protocol">TCP</wikipedia> (où,
  de toute façon, c'est le <wikipedia name="Noyau de système d'exploitation">noyau</wikipedia> qui s'en
  occupe) mais la normalisation de <wikipedia>QUIC</wikipedia> (<rfc
  num="9000" local="true"/>) a suscité davantage d'intérêt pour son
  utilisation avec <wikipedia name="User Datagram
  Protocol">UDP</wikipedia>, où l'application doit gérer cela
  elle-même. Apparemment, un certain nombre de programmereuses ne
  savent pas bien comment mettre de l'ECN sur UDP donc l'auteur de ce
  RFC a testé pour vous, dans le cadre du développement de
  <wikipedia>Chromium</wikipedia>, sur <wikipedia name="Noyau Linux">Linux</wikipedia>,
  <wikipedia>FreeBSD</wikipedia>, <wikipedia>MacOS</wikipedia> et
  <wikipedia name="Microsoft Windows">Windows</wikipedia> (par la suite, je vais surtout parler
  de Linux car c'est ce que j'ai testé ; voyez le RFC pour les autres
  systèmes). Le code de Chromium est <link
  url="https://source.chromium.org/chromium/chromium/src/+/main:net/socket/udp_socket_posix.cc">en
  ligne</link>.</p>
  <p>Si vous avez la flemme de relire le <rfc num="3168"
  local="true"/>, il définit deux bits pour l'ECN, qui se trouvent
  après le champ DSCP (<foreign>Differentiated Services Code
  Point</foreign>, <rfc num="2474" local="true"/>, l'octet qui les contient était
  appelé ToS - <foreign>Type of Service</foreign> - au millénaire
  précédent) de l'en-tête <wikipedia name="Internet
  Protocol">IP</wikipedia>. Si ces deux bits sont à 1, c'est que de la
  congestion a été vue. (Les options UDP du <rfc num="9868"
  local="true"/> sont autre chose, et ne sont pas dans la même
  couche.)</p>
  <p>Outre l'UDP utilisé seul, ce RFC est une bonne lecture si vous
  mettez en œuvre QUIC (<rfc num="9000" local="true"/>), ou <wikipedia
  name="Stream Control Transmission Protocol">SCTP</wikipedia> sur UDP
  (<rfc num="9260" local="true"/>). Pour ce dernier cas, il semble que
  ça n'ait pas encore été utilisé. Il y a aussi <wikipedia
  name="Real-time transport protocol">RTP</wikipedia> sur UDP (<rfc
  num="6679" local="false"/> et <rfc num="8888"/>).</p>
  <p>Ce <wikipedia name="Request for comments">RFC</wikipedia> ne
  développe pas de nouvelles <wikipedia name="Interface de
  programmation">API</wikipedia>, il explique juste ce qu'on peut
  faire avec les API actuelles, en <wikipedia name="C
  (langage)">C</wikipedia> et <wikipedia>C++</wikipedia>.</p>
  <p>Donc, d'abord, si vous voulez <emphasis>recevoir</emphasis> les
  notifications (section 3 du RFC), vous devez utiliser
  <computer><unix>setsockopt</unix></computer> avec les options
  <computer>IP_RECVTOS</computer> (en <wikipedia>IPv4</wikipedia>) ou
  <computer>IP_RECVTCLASS</computer> (en
  <wikipedia>IPv6</wikipedia>) :
  <code>
result =
   setsockopt(socketfd, IPPROTO_IPV6, IPV6_RECVTCLASS, &amp;enable, sizeof(enable));
  </code>
  Il faudra ensuite lire le paquet avec <unix>recvmsg</unix> et
  utiliser les messages auxiliaires avec <unix>cmsg</unix> (type de
  message <computer>IPV6_TCLASS</computer>) :
  <code>
  numbytes = recvmsg(socketfd, &amp;mh, 0);
  for (struct cmsghdr * cmsg = CMSG_FIRSTHDR(&amp;mh);
             cmsg != NULL; cmsg = CMSG_NXTHDR(&amp;mh, cmsg)) {
     if (cmsg->cmsg_level != IPPROTO_IPV6 || cmsg->cmsg_type != IPV6_TCLASS) {
                continue;
     }
     tclass = (int *) CMSG_DATA(cmsg);
  </code>
  (Dans le monde <wikipedia>Apple</wikipedia>, il faudra plutôt lire
  <link
  url="https://developer.apple.com/documentation/network/nwprotocolip/metadata">leur
  documentation</link>.)</p>
  <p>Et pour <emphasis>envoyer</emphasis> de l'ECN ? La section 4 du
  RFC nous dit qu'il faut utiliser
  <computer><unix>setsockopt</unix></computer> pour indiquer la valeur
  (apparemment, il faut écrire l'octet complet, DSCP et ECN) :
  <code>
  result = setsockopt
            (sd, IPPROTO_IPV6, IPV6_TCLASS, &amp;dscp_value, sizeof(dscp_value))
  </code>
  </p>
  <p>Si vous voulez le code complet, je l'ai mis dans les programmes
  <computer><file name="udp-ecn-sender.c"/></computer> et
  <computer><file name="udp-receiver.c"/></computer> (uniquement
  <wikipedia>IPv6</wikipedia>). Ils ont évidemment été écrits <link
  local="unix-network-programming">en lisant le livre de
  Stevens</link> et le <rfc num="3542"/>. Compilons-les et
  testons-les :
  <code>
% make udp-ecn-receiver
cc     udp-ecn-receiver.c   -o udp-ecn-receiver
% ./udp-ecn-receiver 
  </code>
  et dans une autre fenêtre :
  <code>
% make udp-ecn-sender
cc     udp-ecn-sender.c   -o udp-ecn-sender
% ./udp-ecn-sender --verbose ::1
Connecting to ::1 port 4242...
Sending 11 bytes with DSCP 3...
  </code>
  et on voit dans la première fenêtre :
  <code>
Message received, 11 bytes.
Received DSCP 3
  </code>
  Analysé avec <wikipedia name="Wireshark">tshark</wikipedia>, cette
  communication montre :
  <code>
Frame 1: 73 bytes on wire (584 bits), 73 bytes captured (584 bits)
…
Internet Protocol Version 6, Src: ::1, Dst: ::1
…
     .... 0000 0011 .... .... .... .... .... = Traffic Class: 0x03 (DSCP: CS0, ECN: CE)
          .... 0000 00.. .... .... .... .... .... = Differentiated Services Codepoint: Default (0)
        .... .... ..11 .... .... .... .... .... = Explicit Congestion Notification: Congestion Experienced (3)
  </code>
  On voit bien que l'expéditeur a dit qu'il voyait de la
  congestion. Vous pouvez jouer avec l'option
  <computer>--ecn</computer> de l'expéditeur pour essayer d'autres
  valeurs que 3 (les deux bits ECN à 1).</p>
  <p>Ces programmes
  devraient (?) fonctionner sur <wikipedia>FreeBSD</wikipedia> (mais
  je n'ai pas testé). Si vous les portez sur <wikipedia name="Rust
  (langage)">Rust</wikipedia> ou <wikipedia name="Zig
  (langage)">Zig</wikipedia>, le résultat m'intéresse.</p>
</content>
</rfcdesc>
