golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
120.67k stars 17.33k forks source link

x/net/ipv4: calling SetTOS and SetTTL doesn't work as expected on MacOS #67421

Open eelcocramer opened 4 weeks ago

eelcocramer commented 4 weeks ago

Go version

go version go1.22.3 darwin/arm64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='arm64'
GOBIN='/Users/gopher/go/bin'
GOCACHE='/Users/gopher/Library/Caches/go-build'
GOENV='/Users/gopher/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/gopher/go/pkg/mod'
GOOS='darwin'
GOPATH='/Users/gopher/go'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.22.3/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.22.3/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.22.3'
GCCGO='gccgo'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/Users/gopher/reproduce/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/68/ch3mzzs55bz8x9mzk1dsd35c0000gn/T/go-build1659040423=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

package main

import (
    "flag"
    "log"
    "net"

    "golang.org/x/net/ipv4"
)

func main() {
    name := flag.String("i", "eth0", "interface name")
    flag.Parse()

    iface, err := net.InterfaceByName(*name)
    if err != nil {
        log.Fatalf("%v", err)
    }

    group := &net.UDPAddr{IP: net.ParseIP("224.0.0.1"), Port: 1234}
    conn, err := net.ListenPacket("udp4", "224.0.0.1:1234")
    if err != nil {
        log.Fatalf("%v", err)
    }

    p := ipv4.NewPacketConn(conn)
    p.SetMulticastTTL(30)
    p.SetMulticastInterface(iface)

    if err := p.JoinGroup(iface, &net.UDPAddr{IP: group.IP}); err != nil {
        log.Fatalf("%v", err)
    }

    err = p.SetTOS(0x0)
    if err != nil {
        log.Fatalf("%v", err)
    }
    _, err = p.WriteTo([]byte("test1"), nil, group)
    if err != nil {
        log.Fatalf("%v", err)
    }
    err = p.SetTOS(0x20)
    if err != nil {
        log.Fatalf("%v", err)
    }
    _, err = p.WriteTo([]byte("test2"), nil, group)
    if err != nil {
        log.Fatalf("%v", err)
    }
    p.SetMulticastTTL(33)
    err = p.SetTOS(0x40)
    if err != nil {
        log.Fatalf("%v", err)
    }
    _, err = p.WriteTo([]byte("test3"), nil, group)
    if err != nil {
        log.Fatalf("%v", err)
    }
}

What did you see happen?

tcpdump: listening on utun6, link-type NULL (BSD loopback), snapshot length 524288 bytes
09:28:12.021211 IP (tos 0x40, ttl 33, id 32412, offset 0, flags [none], proto UDP (17), length 33)
    139.63.127.166.1234 > 224.0.0.1.1234: [udp sum ok] UDP, length 5
    0x0000:  4540 0021 7e9c 0000 2111 3009 8b3f 7fa6  E@.!~...!.0..?..
    0x0010:  e000 0001 04d2 04d2 000d f26e 7465 7374  ...........ntest
    0x0020:  31                                       1
09:28:12.021339 IP (tos 0x40, ttl 33, id 64306, offset 0, flags [none], proto UDP (17), length 33)
    139.63.127.166.1234 > 224.0.0.1.1234: [udp sum ok] UDP, length 5
    0x0000:  4540 0021 fb32 0000 2111 b372 8b3f 7fa6  E@.!.2..!..r.?..
    0x0010:  e000 0001 04d2 04d2 000d f16e 7465 7374  ...........ntest
    0x0020:  32                                       2
09:28:12.021513 IP (tos 0x40, ttl 33, id 9938, offset 0, flags [none], proto UDP (17), length 33)
    139.63.127.166.1234 > 224.0.0.1.1234: [udp sum ok] UDP, length 5
    0x0000:  4540 0021 26d2 0000 2111 87d3 8b3f 7fa6  E@.!&...!....?..
    0x0010:  e000 0001 04d2 04d2 000d f06e 7465 7374  ...........ntest
    0x0020:  33                                       3

What did you expect to see?

According to the documentation of SetMulticastTTL and SetTOS new values will be applied to "future outgoing multicast packets". However on MacOS the last set value is applied on all outgoing packets that are still in the buffer ((that is an assumption from my side). When I add delays between the subsequent WriteTo calls the tcpdump output shows the correct values for tos and ttl. However, without these delays setting the ToS and TTL could affect previously sent packets.

On Linux the results are as expected from the documentation.

dmitshur commented 4 weeks ago

CC @golang/darwin, @ianlancetaylor.