diff --git a/src/net/interface_linux.go b/src/net/interface_linux.go index 7856dae8fc..66e503f81c 100644 --- a/src/net/interface_linux.go +++ b/src/net/interface_linux.go @@ -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) @@ -255,3 +259,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 +} diff --git a/src/syscall/netlink_linux.go b/src/syscall/netlink_linux.go index 99b5b59825..2417c1100d 100644 --- a/src/syscall/netlink_linux.go +++ b/src/syscall/netlink_linux.go @@ -7,6 +7,8 @@ package syscall import ( + "internal/oserror" + "runtime" "sync" "unsafe" ) @@ -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 {