interfaces_android.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. //go:build android
  2. // Interface enumeration fallback for Android (including Termux).
  3. //
  4. // The Android sandbox (SELinux) blocks the netlink route dumps that
  5. // net.Interfaces()/net.InterfaceAddrs() rely on, so enumeration falls back
  6. // to the SIOCGIFCONF family of ioctls, which the sandbox still permits.
  7. // This file is compiled on android only; every other platform uses the
  8. // standard library directly (see interfaces_default.go).
  9. package network
  10. import (
  11. "errors"
  12. "fmt"
  13. "net"
  14. "unsafe"
  15. "golang.org/x/sys/unix"
  16. )
  17. // Linux ioctl request codes (linux/sockios.h). Values are identical on arm64.
  18. const (
  19. siocGIFCONF = 0x8912
  20. siocGIFFLAGS = 0x8913
  21. siocGIFNETMASK = 0x891b
  22. siocGIFINDEX = 0x8933
  23. )
  24. // ifreqSize is sizeof(struct ifreq) on 64-bit Linux: 16-byte name + 24-byte
  25. // union (sized by struct ifmap). All supported android targets are 64-bit
  26. // (arm64), so a fixed stride is safe.
  27. const ifreqSize = 40
  28. // maxIfconfSize caps the SIOCGIFCONF buffer growth below.
  29. const maxIfconfSize = 1 << 20
  30. // ifreq is the Linux ioctl request struct: 16-byte name + 24-byte union.
  31. type ifreq struct {
  32. name [unix.IFNAMSIZ]byte
  33. union [24]byte
  34. }
  35. // ifconf is the argument struct for SIOCGIFCONF.
  36. type ifconf struct {
  37. length int32
  38. buf uintptr
  39. }
  40. // platformInterfaceFallback enumerates interfaces via ioctls when netlink is
  41. // unavailable.
  42. func platformInterfaceFallback(origErr error) ([]systemInterface, error) {
  43. fallback, err := ioctlSystemInterfaces()
  44. if err != nil {
  45. return nil, fmt.Errorf("failed to get network interfaces: %w", errors.Join(origErr, err))
  46. }
  47. return fallback, nil
  48. }
  49. func ioctl(fd uintptr, req uintptr, arg unsafe.Pointer) error {
  50. if _, _, errno := unix.Syscall(unix.SYS_IOCTL, fd, req, uintptr(arg)); errno != 0 {
  51. return errno
  52. }
  53. return nil
  54. }
  55. // ioctlSystemInterfaces lists interfaces and their primary IPv4 addresses via
  56. // SIOCGIFCONF, plus per-interface flags/index/netmask ioctls.
  57. func ioctlSystemInterfaces() ([]systemInterface, error) {
  58. fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
  59. if err != nil {
  60. return nil, err
  61. }
  62. defer unix.Close(fd)
  63. // SIOCGIFCONF: grow the buffer until it holds the full interface list.
  64. var buf []byte
  65. for size := 4096; ; size *= 2 {
  66. if size > maxIfconfSize {
  67. return nil, errors.New("SIOCGIFCONF interface list exceeds size limit")
  68. }
  69. buf = make([]byte, size)
  70. conf := ifconf{length: int32(size), buf: uintptr(unsafe.Pointer(&buf[0]))}
  71. if err := ioctl(uintptr(fd), siocGIFCONF, unsafe.Pointer(&conf)); err != nil {
  72. return nil, fmt.Errorf("SIOCGIFCONF: %w", err)
  73. }
  74. if conf.length < int32(size) {
  75. buf = buf[:conf.length]
  76. break
  77. }
  78. }
  79. if len(buf) == 0 {
  80. return nil, errors.New("SIOCGIFCONF returned no interfaces")
  81. }
  82. seen := make(map[string]bool)
  83. var out []systemInterface
  84. for off := 0; off+ifreqSize <= len(buf); off += ifreqSize {
  85. name := nullString(buf[off : off+unix.IFNAMSIZ])
  86. if name == "" || seen[name] {
  87. continue
  88. }
  89. seen[name] = true
  90. // Only AF_INET entries carry a usable IPv4 address here.
  91. family := int(buf[off+16]) | int(buf[off+17])<<8
  92. if family != unix.AF_INET {
  93. continue
  94. }
  95. si := systemInterface{}
  96. si.Name = name
  97. si.Index = ioctlIndex(fd, name)
  98. si.Flags = ioctlFlags(fd, name)
  99. ip := net.IPv4(buf[off+20], buf[off+21], buf[off+22], buf[off+23])
  100. mask := ioctlNetmask(fd, name)
  101. if mask == nil {
  102. mask = net.CIDRMask(24, 32)
  103. }
  104. si.addrs = append(si.addrs, &net.IPNet{IP: ip, Mask: mask})
  105. out = append(out, si)
  106. }
  107. if len(out) == 0 {
  108. return nil, errors.New("no AF_INET interfaces found via ioctl")
  109. }
  110. return out, nil
  111. }
  112. func nullString(b []byte) string {
  113. for i, c := range b {
  114. if c == 0 {
  115. return string(b[:i])
  116. }
  117. }
  118. return string(b)
  119. }
  120. func ioctlIndex(fd int, name string) int {
  121. var req ifreq
  122. copy(req.name[:], name)
  123. if ioctl(uintptr(fd), siocGIFINDEX, unsafe.Pointer(&req)) != nil {
  124. return 0
  125. }
  126. idx := uint32(req.union[0]) | uint32(req.union[1])<<8 | uint32(req.union[2])<<16 | uint32(req.union[3])<<24
  127. return int(int32(idx))
  128. }
  129. // ioctlFlags maps the Linux IFF_* bits of the named interface to net.Flags.
  130. func ioctlFlags(fd int, name string) net.Flags {
  131. var req ifreq
  132. copy(req.name[:], name)
  133. if ioctl(uintptr(fd), siocGIFFLAGS, unsafe.Pointer(&req)) != nil {
  134. return 0
  135. }
  136. iff := uint16(req.union[0]) | uint16(req.union[1])<<8
  137. var f net.Flags
  138. if iff&0x0001 != 0 { // IFF_UP
  139. f |= net.FlagUp
  140. }
  141. if iff&0x0002 != 0 { // IFF_BROADCAST
  142. f |= net.FlagBroadcast
  143. }
  144. if iff&0x0008 != 0 { // IFF_LOOPBACK
  145. f |= net.FlagLoopback
  146. }
  147. if iff&0x0010 != 0 { // IFF_POINTOPOINT
  148. f |= net.FlagPointToPoint
  149. }
  150. if iff&0x0040 != 0 { // IFF_RUNNING
  151. f |= net.FlagRunning
  152. }
  153. if iff&0x1000 != 0 { // IFF_MULTICAST
  154. f |= net.FlagMulticast
  155. }
  156. return f
  157. }
  158. func ioctlNetmask(fd int, name string) net.IPMask {
  159. var req ifreq
  160. copy(req.name[:], name)
  161. if ioctl(uintptr(fd), siocGIFNETMASK, unsafe.Pointer(&req)) != nil {
  162. return nil
  163. }
  164. family := int(req.union[0]) | int(req.union[1])<<8
  165. if family != unix.AF_INET {
  166. return nil
  167. }
  168. return net.IPv4Mask(req.union[4], req.union[5], req.union[6], req.union[7])
  169. }