RFC 10055

Configuring UDP Sockets for Explicit Congestion Notification (ECN) for Common Platforms

πŸ“… published October 2026, from ietf

Configuring UDP Sockets for Explicit Congestion Notification (ECN) for Common Platforms ✨

okay bestie, this is RFC 10055 πŸŽ€ It's an Informational doc from the Internet Engineering Task Force (IETF), dated October 2026 (ISSN 2070-1721), written by M. Duke at Google. Informational means it's not a Standards Track spec, so it doesn't set rules for anyone πŸ’… The title block doesn't list anything it obsoletes or updates, so it's standing on its own πŸ¦‹

Abstract

ECN is supposed to work for all transport protocols in principle 🌸 but for UDP it barely got deployed until QUIC got super popular. So the docs for UDP socket APIs for ECN are kinda sparse 😭 This doc records what happened when people experimented with those APIs to get ECN working on UDP for Chromium on Apple, Linux, and Windows platforms πŸ’–

This is a snapshot of how these platforms support ECN at the time it was written πŸ“Έ For newer info, go check each platform's docs, bestie 🫢

Status of This Memo

This document is not an Internet Standards Track specification. It's published for informational purposes ✨

It's a product of the IETF, and it represents the consensus of the IETF community. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). Not every doc the IESG approves is a candidate for any level of Internet Standard, see Section 2 of RFC 7841 πŸ™ˆ

You can find info about this doc's current status, any errata, and how to give feedback at https://www.rfc-editor.org/info/rfc10055 🧚


Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved. πŸ’œ

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, because they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions, and they're provided without warranty as described in the Revised BSD License πŸ“œ

Table of Contents

1.  Introduction
2.  Conventions and Definitions
3.  Receiving ECN Codepoints
  3.1.  Setting the Socket to Report Incoming ECN Codepoints
    3.1.1.  Linux, Apple, and FreeBSD
    3.1.2.  Windows
  3.2.  Retrieving ECN Codepoints on Incoming Packets
    3.2.1.  Linux
    3.2.2.  Apple and FreeBSD
    3.2.3.  Windows
4.  Sending ECN Codepoints
  4.1.  On a Per-Socket Basis
    4.1.1.  Apple, FreeBSD, and Linux
    4.1.2.  Windows
  4.2.  On a Per-Packet Basis
    4.2.1.  Apple, FreeBSD, and Linux
    4.2.2.  Windows
5.  Security Considerations
6.  IANA Considerations
7.  References
  7.1.  Normative References
  7.2.  Informative References
Acknowledgments
Author's Address

1. Introduction 🌈

okay so [RFC3168] defines a 2-bit field in the IP header for ECN, and that field gives network feedback to endpoint congestion controllers πŸ¦‹ Historically this has mostly mattered for TCP [RFC9293]. Any incoming ECN codepoints there are consumed inside the kernel, so the only application interface is turning the capability on or off πŸ™ˆ

The Stream Control Transmission Protocol (SCTP) [RFC9260] has supported ECN in its design for a long time πŸ’… SCTP is sometimes carried over DTLS and UDP [RFC8261]. In principle, user-space implementers could use UDP ECN APIs to pass ECN codepoints between SCTP and the UDP socket. As of publication, the IETF isn't aware of any such efforts πŸ€·β€β™€οΈ

[RFC6679] defines ECN over RTP over UDP. The IETF knows about a research implementation but can't confirm any commercial deployments πŸ”¬ [RFC8888] proposes a different feedback format to work with [RFC6679], and there are a few implementations of it ✨

QUIC [RFC9000] runs over UDP and has been deployed way more widely than SCTP πŸ”₯ The Low Latency, Low Loss, Scalable Throughput (L4S) architecture [RFC9330] and QUIC together have increased interest in ECN over UDP, and it's giving main character energy πŸ‘‘

The Chromium Projects [CHROMIUM] provide a widely deployed protocol library that includes QUIC πŸ“ When they tried to add ECN support for QUIC across all the platforms Chromium runs on, they found that a lot of the ECN-related UDP socket interfaces are poorly documented 😭

So this Informational doc records that experience. The goal is to encourage more ECN support in other QUIC implementations and in any consumer of ECN codepoints that runs over UDP 🫢 It is not a Standards Track doc, and it doesn't bind platforms to any API or suggest any particular API. Iykyk πŸ’€

Lots of socket APIs still talk about the "ToS (Type of Service) byte," including the IP_TOS label, even though [RFC2474] obsoleted that back in 1998 πŸ™ˆ That 8-bit field now holds a 6-bit Differentiated Services Code Point (DSCP) and the 2-bit ECN field. Fun fact, and also a little chaotic 🧚

This document focuses on the APIs for the C and C++ languages. Other languages will likely have different syntax and capabilities πŸ’–

The "ecn-examples" code repository [EXAMPLES] is extremely compact code that can verify the information in this document. Literally so tiny, and it proves the doc is legit πŸ”₯

This document covers access to the ECN field in the IP header through socket APIs. It does not cover UDP transport-layer options [RFC9868]. Those are a separate extension mechanism that works at a different layer, so don't mix them up, bestie πŸ“

2. Conventions and Definitions πŸŽ€

This document is not a general tutorial on UDP socket programming, and it does assume you already know basic socket concepts like binding, socket options, and common system error codes πŸ“š

Throughout this document, "Apple" refers to both macOS and iOS 🍎 "Linux" guidance also applies to Android πŸ€–

3. Receiving ECN Codepoints πŸ¦‹

Network devices can change the ECN codepoint in the IP header πŸ‘€ Since this feedback is required at the packet sender, the packet receiver has to pull this codepoint out of the UDP socket so it can report it back to the sender. It's giving full circle energy πŸ”„

There are two parts to this. Part 1 is setting the socket to report incoming ECN marks. Part 2 is retrieving the ECN codepoint for each incoming packet πŸ’…

Apple platforms also provide a framework for network connections that lets you receive ECN flags when using UDP without the traditional socket option semantics. When sending or receiving UDP datagrams, IP protocol metadata carries ECN information in both directions. See [APPLE-NETWORK-FRAMEWORK] 🍏

3.1. Setting the Socket to Report Incoming ECN Codepoints πŸ’–

3.1.1. Linux, Apple, and FreeBSD

To receive ECN codepoints, applications set a socket option to "true" using a setsockopt() call πŸŽ€

On all platforms, IPv4 sockets require the IPPROTO_IP-level socket option named IP_RECVTOS to be set πŸ’œ

On all platforms, IPv6 sockets require the IPPROTO_IPV6-level socket option named IPV6_RECVTCLASS to be set πŸ’œ

If the IPv6 socket is not IPv6 only, Linux hosts also need the IPPROTO_IP-level socket option IP_RECVTOS set in order to receive ECN codepoints for UDP/IPv4 packets. This step isn't necessary on Apple or FreeBSD πŸ™ˆ

At the time of writing, you can find an example implementation at [CHROMIUM-POSIX] ✨

3.1.2. Windows

Windows documentation recommends using the function WSASetRecvIPEcn() to enable ECN codepoint reporting regardless of IP version πŸͺŸ This function dates to Windows 10 Build 20348, according to [WINDOWS-DOC] πŸ“…

You can also do it by calling setsockopt() with the IPPROTO_IP level and the name IP_RECVECN for IPv4, and the IPPROTO_IPV6 level and the name IPV6_RECVECN for IPv6. These options are documented at [WINDOWS-SOCKOPT] πŸ“–

For IPv6 sockets that are not IPv6 only, WSASetRecvIPEcn() will not enable ECN reporting for IPv4. That needs a separate setsockopt() call using the IP_RECVECN option 🫠

If a socket is bound to an IPv4-mapped IPv6 address (that's the format ::ffff:<IPv4 address>, see Section 2.5.5.2 of [RFC4291]), then calls to WSASetRecvIpEcn() return the error EINVAL 😭 These sockets should instead use an explicit setsockopt() call to set IP_RECVECN. Just a heads up for future you 🧚

At the time of writing, you can find an example implementation at [CHROMIUM-WINDOWS] πŸ’…

3.2. Retrieving ECN Codepoints on Incoming Packets πŸ“¦

All platforms in this document require a recvmsg() call to read data from the socket and to retrieve the ECN codepoint, because that info comes as ancillary data 🎁 They all return zero or more "cmsg"s that contain the requested info about the arriving packet ✨

You can find examples of the technique described below at [CHROMIUM-POSIX] and [CHROMIUM-WINDOWS] 🫢

3.2.1. Linux

If a UDP/IPv4 message is received, Linux includes a cmsg of level IPPROTO_IP and type IP_TOS. The cmsg data contains an unsigned char. This applies to IPv4 sockets and to IPv6 sockets that are not IPv6 only 🐣

If a UDP/IPv6 message is received, Linux includes a cmsg of level IPPROTO_IPV6 and type IPV6_TCLASS. The cmsg data contains an int. This applies to IPv6 sockets πŸ¦‹

The cmsg data contains the entire IP header byte, which includes the DSCP and the ECN codepoint. The ECN codepoint is the two least significant bits of that byte πŸ’œ

The same applies to the Linux-specific recvmmsg() call πŸ”₯

3.2.2. Apple and FreeBSD

If a UDP/IPv4 message is received on an IPv4 socket, the ancillary data contains a cmsg of level IPPROTO_IP and type IP_RECVTOS. The cmsg data contains an unsigned char 🍎

If a UDP/IPv6 or UDP/IPv4 message is received on an IPv6 socket, the ancillary data contains a cmsg of level IPPROTO_IPV6 and type IPV6_TCLASS. The cmsg data contains an int 🌸

The cmsg data contains the entire IP header byte, which includes the DSCP and the ECN codepoint. The ECN codepoint is the two least significant bits of that byte πŸ’…

3.2.3. Windows

If the incoming packet is UDP/IPv4, the socket includes a cmsg of level IPPROTO_IP and type IP_ECN. The cmsg data contains an int πŸͺŸ

If the incoming packet is UDP/IPv6, the socket includes a cmsg of level IPPROTO_IPV6 and type IPV6_ECN. The cmsg data contains an int πŸͺŸ

The cmsg data solely consists of the ECN codepoint, so it needs no further bitwise operations. Easy mode, bestie 😌

4. Sending ECN Codepoints πŸš€

The existing ECN specs ([RFC3168] and [RFC9330]) picture a particular connection that's consistently sending the same ECN codepoint 🎯 It might change that marking after completing a handshake successfully, for example after recognizing that the path or the peer doesn't support ECN, or when it moves to a new path πŸ›€οΈ So using a socket option to configure a consistent marking is generally more resource efficient πŸ’–

But some server designs receive all incoming packets on a single socket 🌊 Since the many connections in that packet stream might have different levels of ECN support, it makes sense to provide the ECN codepoint on a per-packet basis 🍰

Apple platforms also provide a framework for network connections that lets you send ECN flags when using UDP without the traditional socket option semantics. When sending or receiving UDP datagrams, IP protocol metadata carries ECN information in both directions. See [APPLE-NETWORK-FRAMEWORK] 🍏

4.1. On a Per-Socket Basis 🌷

4.1.1. Apple, FreeBSD, and Linux πŸ“

okay bestie, this is where we set the ECN codepoint on the whole socket πŸ’… For sending UDP/IPv4 packets on an IPv4 socket, Apple, FreeBSD, and Linux all let you configure the outgoing ECN codepoint with the IPPROTO_IP-level socket option named IP_TOS. The value is type int πŸŽ€

For sending UDP/IPv6 packets on an IPv6 socket, those same three platforms use the IPPROTO_IPV6-level socket option named IPV6_TCLASS to configure the outgoing ECN codepoint. The value is also type int πŸ¦‹

Now the messy part, ngl 😭 For UDP/IPv4 packets sent on an IPv6 socket, it depends on the platform:

  • Linux uses the IPPROTO_IP-level socket option named IP_TOS.
  • Apple and FreeBSD use the IPPROTO_IPV6-level socket option named IPV6_TCLASS.

Except for Apple platforms, this setsockopt() call also sets the DSCP that comprises the rest of the header byte. Applications making this call ought to preserve any existing DSCP setting, which might require an additional getsockopt() call, to avoid overriding commands set by other code in the stack. If there are multiple threads making changes to this byte, further safeguards will be necessary.

Heads up, bestie: that DSCP stuff can totally clobber what other code in your stack set up if you're not careful πŸ™ˆ

An example of the technique described above can be found at [CHROMIUM-POSIX].

4.1.2. Windows 🌸

Sadly, Windows is giving a little less πŸ˜” At the time of this writing, Windows does not provide a way to configure marking on a per-socket basis.

4.2. On a Per-Packet Basis πŸ’œ

Okay, now we get to go granular! Packets can be individually marked with ECN codepoints using the ancillary data that accompanies a sendmsg() call. Main character energy πŸ‘‘

4.2.1. Apple, FreeBSD, and Linux 🧚

For sending UDP/IPv4 packets on an IPv4 socket, Apple, FreeBSD, and Linux all use a cmsg with level IPPROTO_IP and type IP_TOS. On Apple and Linux, the data type is int, but on FreeBSD it's unsigned char. Different vibes, same goal ✨

For sending UDP/IPv6 packets on an IPv6 socket, Apple, FreeBSD, and Linux use a cmsg with level IPPROTO_IPV6 and type IPV6_TCLASS. The data type is int πŸ’–

For sending UDP/IPv4 packets on an IPv6 socket, it's the same split as before:

  • Linux requires a cmsg with level IPPROTO_IP and type IP_TOS.
  • Apple and FreeBSD accept a cmsg with level IPPROTO_IPV6 and type IPV6_TCLASS.

The same applies to the Linux-specific sendmmsg() call 🌈

4.2.2. Windows 🌈

For IPv4 packets, Windows uses a cmsg with level IPPROTO_IP and type IP_ECN πŸ¦‹

For IPv6 packets, Windows uses a cmsg with level IPPROTO_IPV6 and type IPV6_ECN ⭐

An example of the technique described above can be found at [CHROMIUM-WINDOWS].


5. Security Considerations πŸ”₯

The security implications of ECN are documented in [RFC3168] and [RFC9330]. This document is a guide to enabling these capabilities, which incurs no additional security considerations. Phew, low key relieving πŸ™

Now the serious bit, bestie. Implementing ECN capabilities on some platforms but not others can help peers identify the operating system in use by a host. That can have privacy implications, so take it seriously 🫣

This document aims to mitigate that possibility πŸ›‘οΈ

6. IANA Considerations 🫢

Good news: this document has no IANA actions πŸŽ€

7. References πŸ“š

7.1. Normative References

   [RFC2474]  Nichols, K., Blake, S., Baker, F., and D. Black,
              "Definition of the Differentiated Services Field (DS
              Field) in the IPv4 and IPv6 Headers", RFC 2474,
              DOI 10.17487/RFC2474, December 1998,
              <https://www.rfc-editor.org/info/rfc2474>.

   [RFC3168]  Ramakrishnan, K., Floyd, S., and D. Black, "The Addition
              of Explicit Congestion Notification (ECN) to IP",
              RFC 3168, DOI 10.17487/RFC3168, September 2001,
              <https://www.rfc-editor.org/info/rfc3168>.

   [RFC9330]  Briscoe, B., Ed., De Schepper, K., Bagnulo, M., and G.
              White, "Low Latency, Low Loss, and Scalable Throughput
              (L4S) Internet Service: Architecture", RFC 9330,
              DOI 10.17487/RFC9330, January 2023,
              <https://www.rfc-editor.org/info/rfc9330>.

7.2. Informative References

   [APPLE-NETWORK-FRAMEWORK]
              "NWProtocolIP.Metadata",
              <https://developer.apple.com/documentation/network/
              nwprotocolip/metadata>.

   [CHROMIUM] "The Chromium Projects",
              <https://www.chromium.org/chromium-projects/>.

   [CHROMIUM-POSIX]
              "udp_socket_posix.cc", commit 6bdc14d, 19 June 2026,
              <https://source.chromium.org/chromium/chromium/
              src/+/main:net/socket/udp_socket_posix.cc>.

   [CHROMIUM-WINDOWS]
              "udp_socket_win.cc", commit 85a1d1c, 10 June 2026,
              <https://source.chromium.org/chromium/chromium/
              src/+/main:net/socket/udp_socket_win.cc>.

   [EXAMPLES] "ecn-examples", commit 39e39bf, 1 December 2025,
              <https://github.com/nplab/ecn-examples>.

   [RFC4291]  Hinden, R. and S. Deering, "IP Version 6 Addressing
              Architecture", RFC 4291, DOI 10.17487/RFC4291, February
              2006, <https://www.rfc-editor.org/info/rfc4291>.

   [RFC6679]  Westerlund, M., Johansson, I., Perkins, C., O'Hanlon, P.,
              and K. Carlberg, "Explicit Congestion Notification (ECN)
              for RTP over UDP", RFC 6679, DOI 10.17487/RFC6679, August
              2012, <https://www.rfc-editor.org/info/rfc6679>.

   [RFC8261]  Tuexen, M., Stewart, R., Jesup, R., and S. Loreto,
              "Datagram Transport Layer Security (DTLS) Encapsulation of
              SCTP Packets", RFC 8261, DOI 10.17487/RFC8261, November
              2017, <https://www.rfc-editor.org/info/rfc8261>.

   [RFC8888]  Sarker, Z., Perkins, C., Singh, V., and M. Ramalho, "RTP
              Control Protocol (RTCP) Feedback for Congestion Control",
              RFC 8888, DOI 10.17487/RFC8888, January 2021,
              <https://www.rfc-editor.org/info/rfc8888>.

   [RFC9000]  Iyengar, J., Ed. and M. Thomson, Ed., "QUIC: A UDP-Based
              Multiplexed and Secure Transport", RFC 9000,
              DOI 10.17487/RFC9000, May 2021,
              <https://www.rfc-editor.org/info/rfc9000>.

   [RFC9260]  Stewart, R., TΓΌxen, M., and K. Nielsen, "Stream Control
              Transmission Protocol", RFC 9260, DOI 10.17487/RFC9260,
              June 2022, <https://www.rfc-editor.org/info/rfc9260>.

   [RFC9293]  Eddy, W., Ed., "Transmission Control Protocol (TCP)",
              STD 7, RFC 9293, DOI 10.17487/RFC9293, August 2022,
              <https://www.rfc-editor.org/info/rfc9293>.

   [RFC9868]  Touch, J. and C. Heard, Ed., "Transport Options for UDP",
              RFC 9868, DOI 10.17487/RFC9868, October 2025,
              <https://www.rfc-editor.org/info/rfc9868>.

   [WINDOWS-DOC]
              "WSASetRecvIPEcn function (ws2tcpip.h)", 22 February 2024,
              <https://learn.microsoft.com/en-
              us/windows/win32/api/ws2tcpip/nf-ws2tcpip-
              wsasetrecvipecn>.

   [WINDOWS-SOCKOPT]
              "MSDN - IPPROTO_IP socket options", 7 May 2026,
              <https://learn.microsoft.com/en-us/windows/win32/winsock/
              ipproto-ip-socket-options>.

Acknowledgments 🫢

Big thank-yous, bestie! The author would like to thank Ryan Hamilton, who gave constant advice through this whole effort. Randall Meyer from Apple and Nick Grifka from Microsoft gave useful hints about how their respective operating systems behave. However, the author takes full responsibility for any errors above πŸ’…

Michael Tüxen wrote the ecn-examples code, which was super helpful in verifying the conclusions in this document. He also made multiple editorial contributions 🌸

Neal Cardwell, Gorry Fairhurst, Max Franke, Rodney Grimes, Will Hawkins, Guillaume HΓ©tier, Max Inden, Jonathan Lennox, Colin Perkins, Marten Seemann, and Greg White all made improvements to this document πŸ’œ

Author's Address πŸ’Œ

   Martin Duke
   Google
   Email: martin.h.duke@gmail.com
πŸ“¦ where this page comes from