golang / go

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

x/mobile: Calling net.Listen fails on Android 11+ #68082

Open wlynxg opened 3 months ago

wlynxg commented 3 months ago

Go version

go version go1.20.11 darwin/arm64

Output of go env in your module/workspace:

GO111MODULE="on"
GOARCH="arm64"
GOBIN=""
GOCACHE="/Users/xxxx/Library/Caches/go-build"
GOENV="/Users/xxxx/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="arm64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/xxxx/go/pkg/mod"
GONOPROXY="sum.golang.google.cn"
GONOSUMDB="sum.golang.google.cn"
GOOS="darwin"
GOPATH="/Users/xxxx/go"
GOPRIVATE=""
GOPROXY="https://goproxy.cn,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_arm64"
GOVCS=""
GOVERSION="go1.20.11"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/xxxx/Desktop/xxxx/gomobile/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 -fdebug-prefix-map=/var/folders/tl/pjq29cdd6s53760n3zjwwr3c0000gn/T/go-build3441012208=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

package mobile

import (
    "fmt"
    "log"
    "net"
    "net/netip"
    "syscall"

    "github.com/wlynxg/anet"
)

func Greetings(name string) string {
    anet.SetAndroidVersion(14)
    allifaces, err := anet.Interfaces()
    if err != nil {
        return fmt.Sprintf("Error: %s!", err)
    }
    s := ""

    for _, iface := range allifaces {
        s += iface.Name
        addrs, err := anet.InterfaceAddrsByInterface(&iface)
        if err != nil {
            s += fmt.Sprintf("Error: %s", err) + "\n"
        } else {
            s += fmt.Sprintf("%s \n", addrs)
        }

        for _, addr := range addrs {
            parseAddr, err := netip.ParsePrefix(addr.String())
            if err != nil {
                continue
            }

            if parseAddr.Addr().Is4() || parseAddr.Addr().IsMulticast() {
                continue
            }

            addr := fmt.Sprintf("[%s%%%s]:0", parseAddr.Addr().String(), iface.Name)

            _, err = net.Listen("tcp", addr)
            if err != nil {
                log.Printf("listen %s error: %s", addr, err)
            }
        }
    }
    return fmt.Sprintf("Hello, %s!", s)
}

What did you see happen?

listen [fe80::d461:24ff:fe95:d487%wlan0]:0 error: listen tcp [fe80::d461:24ff:fe95:d487%wlan0]:0: bind: invalid argument
listen [fe80::b6ee:d454:7f5b:6fe%rmnet_data1]:0 error: listen tcp [fe80::b6ee:d454:7f5b:6fe%rmnet_data1]:0: bind: invalid argument
listen [fe80::64bd:d9ff:fe5b:92a6%dummy0]:0 error: listen tcp [fe80::64bd:d9ff:fe5b:92a6%dummy0]:0: bind: invalid argument
listen [fe80::e0f1:31ff:fe88:8eb%ifb0]:0 error: listen tcp [fe80::e0f1:31ff:fe88:8eb%ifb0]:0: bind: invalid argument
listen [fe80::6cbb:96ff:fe13:ff68%ifb1]:0 error: listen tcp [fe80::6cbb:96ff:fe13:ff68%ifb1]:0: bind: invalid argument
listen [fe80::b0a1:c3ff:fe6d:c67%ifb2]:0 error: listen tcp [fe80::b0a1:c3ff:fe6d:c67%ifb2]:0: bind: invalid argument
listen [fe80::d41b:7750:b2d1:8fc6%rmnet_data0]:0 error: listen tcp [fe80::d41b:7750:b2d1:8fc6%rmnet_data0]:0: bind: invalid argument
listen [fe80::84bb:a6ff:cd2d:2ff1%rmnet_data3]:0 error: listen tcp [fe80::84bb:a6ff:cd2d:2ff1%rmnet_data3]:0: bind: invalid argument
listen [fe80::5bec:aaaf:e221:d681%rmnet_data4]:0 error: listen tcp [fe80::5bec:aaaf:e221:d681%rmnet_data4]:0: bind: invalid argument

What did you expect to see?

listen success

gabyhelp commented 3 months ago

Similar Issues

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

wlynxg commented 3 months ago

The error comes from https://github.com/torvalds/linux/blob/e5b3efbe1ab1793bb49ae07d56d0973267e65112/net/ipv6/af_inet6.c#L364

In my code, I specified the zone name, and go needs to obtain the corresponding relationship between zone name and zone index from zoneCache when parsing. The data in zoneCache comes from https://github.com/golang/go/blob/4f77a83589baa5a3038cc6e35615084dc7e5f0c1/src/net/interface.go#L217, but because there is a problem when go calls interfaceTable on the Android side https://github.com/golang/go/issues/40569, go fails when doing zone name -> zone index mapping, so in order to pass a valid zone id to the socket

I fixed this problem in anet: https://github.com/wlynxg/anet/commit/a8525cbb183e42802dcf57e39bd1daf26e771050

Zxilly commented 3 months ago

It might be possible to commit these changes to the standard library.

wlynxg commented 3 months ago

It might be possible to commit these changes to the standard library.

This is difficult in some ways because the changes are tied to the Android version.

vikulin commented 3 months ago

The error comes from https://github.com/torvalds/linux/blob/e5b3efbe1ab1793bb49ae07d56d0973267e65112/net/ipv6/af_inet6.c#L364

In my code, I specified the zone name, and go needs to obtain the corresponding relationship between zone name and zone index from zoneCache when parsing. The data in zoneCache comes from

https://github.com/golang/go/blob/4f77a83589baa5a3038cc6e35615084dc7e5f0c1/src/net/interface.go#L217 , but because there is a problem when go calls interfaceTable on the Android side #40569, go fails when doing zone name -> zone index mapping, so in order to pass a valid zone id to the socket

I fixed this problem in anet: wlynxg/anet@a8525cb

How to get zone id ? I noticed that Android works with scope id for link local addresses not with zone name: https://developer.android.com/reference/java/net/Inet6Address

wlynxg commented 3 months ago

@vikulin The zone id is actually the index of the interface https://github.com/golang/go/blob/9d33956503c0d96c0c5666d374173f7ac9756d98/src/net/interface.go#L43. In fact, when passing the zone id, you can choose the name https://github.com/golang/go/blob/9d33956503c0d96c0c5666d374173f7ac9756d98/src/net/interface.go#L263 or directly pass the index https://github.com/golang/go/blob/9d33956503c0d96c0c5666d374173f7ac9756d98/src/net/interface.go#L267

vikulin commented 3 months ago

@wlynxg I'm not sure that zone id is what I'm looking for. Obviously zone id = 46 is not the index since the Android device never had so many interfaces. Linux devices return interface names - this is where the difference. Code is expecting interface name but getting scope id.

nBytes, rcm, fromAddr, err := m.sock.ReadFrom(bs)
from := fromAddr.(*net.UDPAddr)
log.Infoln(from.String())

returns

[fe80::33d6:4970:9c23:5f7c%46]:9001
wlynxg commented 3 months ago

@vikulin The network card index numbers do not necessarily need to be in order, as long as they are unique. You can make your own rules. You can directly output iface.Index to view their ids. When using c for ipv6 socket, the zone id) passed is the index of the network.

dr2chase commented 3 months ago

@hyangah @ianlancetaylor