How to read (and write) the ECN bits?

497 views
Skip to first unread message

marten...@gmail.com

unread,
Aug 5, 2020, 2:30:31 PM8/5/20
to golang-nuts
I'm working on a Go implementation of the QUIC protocol (https://github.com/lucas-clemente/quic-go). QUIC specifies how to use ECN (Explicit Congestion Notification) to detect and respond to congestion in the network (see https://tools.ietf.org/html/draft-ietf-quic-transport-29#section-13.4 for details on that).

As far as I can see, there's no way to read (and write) the ECN bits from the IP header, unless I use a raw socket, which would be a suboptimal solution for many reasons.
The closest I could get to extracting information from the IP header was by using UDPConn.ReadFromUDP and then using golang.org/x/net/ipv4 to parse the oob bytes into an ipv4.ControlMessage (or equivalently for IPv6). This at least gives me access to the TTL field. It seems like this approach is insufficient to get access to the ECN bits though.

Can anyone help me with this?

Andrei Tudor Călin

unread,
Aug 5, 2020, 5:33:41 PM8/5/20
to marten...@gmail.com, golang-nuts
ECN bits are represented in the TOS field. I think you can use setsockopt with the IP_TOS option to set the TOS field on a socket. See ip(7). On the Go side, use the SyscallConn method on your UDPConn, then call setsockopt using the Control method. Something like this (untested): https://play.golang.org/p/6_R-zlBSibv

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/3c96ecc4-e507-48d1-a912-9e3caf6ecd11o%40googlegroups.com.


--
Andrei Călin

Uli Kunitz

unread,
Aug 6, 2020, 10:02:12 AM8/6/20
to golang-nuts
Reading is possible with IP_RECVTOS on Linux but requires the use of recvmsg, because the TOS field is provided as ancillary data. This wouldn't be very portable though. Raw sockets with IP_HDRINCL are a better option if portability is a concern.

David Finkel

unread,
Aug 6, 2020, 12:52:46 PM8/6/20
to Uli Kunitz, golang-nuts
On Thu, Aug 6, 2020 at 10:02 AM Uli Kunitz <uli.k...@gmail.com> wrote:
Reading is possible with IP_RECVTOS on Linux but requires the use of recvmsg, because the TOS field is provided as ancillary data. This wouldn't be very portable though. Raw sockets with IP_HDRINCL are a better option if portability is a concern.
Keep in mind that raw sockets are privileged under Linux. (the process must have CAP_NET_RAW either by being root, or some other mechanism)
That vastly limits the usefulness of a QUIC implementation.

On Wednesday, August 5, 2020 at 11:33:41 PM UTC+2 ma...@acln.ro wrote:
ECN bits are represented in the TOS field. I think you can use setsockopt with the IP_TOS option to set the TOS field on a socket. See ip(7). On the Go side, use the SyscallConn method on your UDPConn, then call setsockopt using the Control method. Something like this (untested): https://play.golang.org/p/6_R-zlBSibv

On Wed, Aug 5, 2020 at 9:30 PM <marten...@gmail.com> wrote:
I'm working on a Go implementation of the QUIC protocol (https://github.com/lucas-clemente/quic-go). QUIC specifies how to use ECN (Explicit Congestion Notification) to detect and respond to congestion in the network (see https://tools.ietf.org/html/draft-ietf-quic-transport-29#section-13.4 for details on that).

As far as I can see, there's no way to read (and write) the ECN bits from the IP header, unless I use a raw socket, which would be a suboptimal solution for many reasons.
The closest I could get to extracting information from the IP header was by using UDPConn.ReadFromUDP and then using golang.org/x/net/ipv4 to parse the oob bytes into an ipv4.ControlMessage (or equivalently for IPv6). This at least gives me access to the TTL field. It seems like this approach is insufficient to get access to the ECN bits though.

Can anyone help me with this?

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/3c96ecc4-e507-48d1-a912-9e3caf6ecd11o%40googlegroups.com.


--
Andrei Călin

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Marten Seemann

unread,
Aug 7, 2020, 8:21:29 AM8/7/20
to golang-nuts
Thank you guys, your tips were really helpful!

Here's the solution I came up with for reading the ECN bits:
After setting the IP_RECVTOS options
syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_RECVTOS, 1)
one can read packets from the socket using
n, oobn, flags, addr, err := conn.ReadMsgUDP(b, oob)
oob now contains all the control messages, which can be parsed using
ctrlMsgs, err := syscall.ParseSocketControlMessage(oob[:oobn])
The ECN bits can now be extracted by searching for the IP_TOS control message:
for _, ctrlMsg := range ctrlMsgs {
    if ctrlMsg.Header.Type == syscall.IP_TOS {
        ecn := ctrlMsg.Data[0] & 0x3
    }
}

This code works, it's just a bit unfortunate that parsing the control messages causes quite a few allocations, especially as this is code that's run per packet. One would have to benchmark the performance impact of this. For accessing the ECN bits it's probably not too hard to write a specialized parser that doesn't allocate at all.
Reply all
Reply to author
Forward
0 new messages