Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/mobile: fix net.Interface() and net.InterfaceAddrs() in Android11 version #61089

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 159 additions & 1 deletion src/net/interface_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ package net

import (
"os"
"runtime"
"syscall"
"unsafe"
)

// If the ifindex is zero, interfaceTable returns mappings of all
// network interfaces. Otherwise it returns a mapping of a specific
// network interfaces. Otherwise, it returns a mapping of a specific
// interface.
func interfaceTable(ifindex int) ([]Interface, error) {
tab, err := syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC)
if err != nil {
if runtime.GOOS == "android" && os.IsPermission(err) {
return interfaceTableAndroid(ifindex)
}
return nil, os.NewSyscallError("netlinkrib", err)
}
msgs, err := syscall.ParseNetlinkMessage(tab)
Expand Down Expand Up @@ -270,3 +274,157 @@ func parseProcNetIGMP6(path string, ifi *Interface) []Addr {
}
return ifmat
}

// Starting from Android 11, it is no longer possible to retrieve network card information
// using the RTM_GETLINK method.
// As a result, alternative methods need to be employed.
// After considering the Android NetworkInterface.getNetworkInterfaces() method,
// I opted to utilize the RTM_GETADDR + ioctl approach to obtain network card information.
// However, it appears that retrieving the
// HWAddr (hardware address) of the network card is currently not achievable.
func interfaceTableAndroid(ifindex int) ([]Interface, error) {
tab, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
if err != nil {
return nil, os.NewSyscallError("netlinkrib", err)
}
msgs, err := syscall.ParseNetlinkMessage(tab)
if err != nil {
return nil, os.NewSyscallError("parsenetlinkmessage", err)
}

var ift []Interface
im := make(map[uint32]struct{})
loop:
for _, m := range msgs {
switch m.Header.Type {
case syscall.NLMSG_DONE:
break loop
case syscall.RTM_NEWADDR:
ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
if _, ok := im[ifam.Index]; ok {
continue
} else {
im[ifam.Index] = struct{}{}
}

if ifindex == 0 || ifindex == int(ifam.Index) {
ifi := newLinkAndroid(ifam)
if ifi != nil {
ift = append(ift, *ifi)
}
if ifindex == int(ifam.Index) {
break loop
}
}
}
}

return ift, nil
}

// According to the network card Index, get the Name, MTU and Flags of the network card through ioctl
func newLinkAndroid(ifam *syscall.IfAddrmsg) *Interface {
ift := &Interface{Index: int(ifam.Index)}

name, err := indexToName(ifam.Index)
if err != nil {
return nil
}
ift.Name = name

mtu, err := nameToMTU(name)
if err != nil {
return nil
}
ift.MTU = mtu

flags, err := nameToFlags(name)
if err != nil {
return nil
}
ift.Flags = flags
return ift
}

func ioctl(fd int, req uint, arg unsafe.Pointer) error {
_, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg))
if e1 != 0 {
return e1
}
return nil
}

func indexToName(index uint32) (string, error) {
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM|syscall.SOCK_CLOEXEC, 0)
if err != nil {
return "", err
}
defer syscall.Close(fd)

var ifr [40]byte
*(*uint32)(unsafe.Pointer(&ifr[syscall.IFNAMSIZ])) = index
err = ioctl(fd, syscall.SIOCGIFNAME, unsafe.Pointer(&ifr[0]))
if err != nil {
return "", err
}

return string(trim(ifr[:syscall.IFNAMSIZ])), nil
}

func nameToMTU(name string) (int, error) {
// Leave room for terminating NULL byte.
if len(name) >= syscall.IFNAMSIZ {
return 0, syscall.EINVAL
}

fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM|syscall.SOCK_CLOEXEC, 0)
if err != nil {
return 0, err
}
defer syscall.Close(fd)

var ifr [40]byte
copy(ifr[:], name)
err = ioctl(fd, syscall.SIOCGIFMTU, unsafe.Pointer(&ifr[0]))
if err != nil {
return 0, err
}

return int(*(*int32)(unsafe.Pointer(&ifr[syscall.IFNAMSIZ]))), nil
}

func nameToFlags(name string) (Flags, error) {
// Leave room for terminating NULL byte.
if len(name) >= syscall.IFNAMSIZ {
return 0, syscall.EINVAL
}

fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM|syscall.SOCK_CLOEXEC, 0)
if err != nil {
return 0, err
}
defer syscall.Close(fd)

var ifr [40]byte
copy(ifr[:], name)
err = ioctl(fd, syscall.SIOCGIFFLAGS, unsafe.Pointer(&ifr[0]))
if err != nil {
return 0, err
}

return linkFlags(*(*uint32)(unsafe.Pointer(&ifr[syscall.IFNAMSIZ]))), nil
}

func trim(data []byte) []byte {
if len(data) == 0 {
return nil
}

index := len(data) - 1

for ; index > 0 && data[index] == 0; index-- {
}
result := make([]byte, index+1)
copy(result, data)
return result
}
11 changes: 10 additions & 1 deletion src/syscall/netlink_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
package syscall

import (
"internal/oserror"
"runtime"
"sync"
"unsafe"
)
Expand Down Expand Up @@ -65,7 +67,14 @@ func NetlinkRIB(proto, family int) ([]byte, error) {
defer Close(s)
sa := &SockaddrNetlink{Family: AF_NETLINK}
if err := Bind(s, sa); err != nil {
return nil, err
// Bind operation of Netlink socket is prohibited in Android11 and later versions
if runtime.GOOS != "android" {
return nil, err
}

if e, ok := err.(Errno); !ok && !e.Is(oserror.ErrPermission) {
return nil, err
}
}
wb := newNetlinkRouteRequest(proto, 1, family)
if err := Sendto(s, wb, 0, sa); err != nil {
Expand Down