localaddrs_android.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. //go:build android
  2. package nebula
  3. import (
  4. "errors"
  5. "fmt"
  6. "net"
  7. "os"
  8. "slices"
  9. "syscall"
  10. "unsafe"
  11. "golang.org/x/sys/unix"
  12. )
  13. // The Android app sandbox denies bind() on netlink route sockets and RTM_GETLINK, so
  14. // net.Interfaces fails with EACCES. Enumerate with an RTM_GETADDR dump on an unbound socket instead.
  15. func localInterfaces() ([]localInterface, error) {
  16. return netlinkInterfaceAddrs()
  17. }
  18. // netlinkInterfaceAddrs returns every interface that has an address, with its addresses. It dumps
  19. // RTM_GETADDR on a netlink socket that is never bound and names each index with SIOCGIFNAME, the
  20. // same operations Bionic's getifaddrs uses for app UIDs, which the sandbox permits.
  21. func netlinkInterfaceAddrs() ([]localInterface, error) {
  22. dump, err := netlinkAddrDump()
  23. if err != nil {
  24. return nil, err
  25. }
  26. msgs, err := syscall.ParseNetlinkMessage(dump)
  27. if err != nil {
  28. return nil, err
  29. }
  30. // Any socket will do for SIOCGIFNAME.
  31. sfd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
  32. if err != nil {
  33. return nil, os.NewSyscallError("socket", err)
  34. }
  35. defer unix.Close(sfd)
  36. type iface struct {
  37. index uint32
  38. name string
  39. addrs []net.Addr
  40. }
  41. var ifaces []*iface
  42. byIndex := make(map[uint32]*iface) // nil marks an index that vanished before we could name it
  43. for _, m := range msgs {
  44. if m.Header.Type != unix.RTM_NEWADDR {
  45. continue
  46. }
  47. if len(m.Data) < unix.SizeofIfAddrmsg {
  48. return nil, fmt.Errorf("short RTM_NEWADDR message: %d bytes", len(m.Data))
  49. }
  50. ifam := (*unix.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
  51. i, seen := byIndex[ifam.Index]
  52. if !seen {
  53. name, err := interfaceNameByIndex(sfd, ifam.Index)
  54. if err != nil && !errors.Is(err, unix.ENODEV) {
  55. return nil, fmt.Errorf("failed to resolve name of interface %d: %w", ifam.Index, err)
  56. }
  57. if err == nil {
  58. i = &iface{index: ifam.Index, name: name}
  59. ifaces = append(ifaces, i)
  60. }
  61. byIndex[ifam.Index] = i
  62. }
  63. if i == nil {
  64. // The interface went away between the dump and the ioctl.
  65. continue
  66. }
  67. attrs, err := syscall.ParseNetlinkRouteAttr(&m)
  68. if err != nil {
  69. return nil, err
  70. }
  71. if a := newNetlinkAddr(ifam, attrs); a != nil {
  72. i.addrs = append(i.addrs, a)
  73. }
  74. }
  75. // The dump is grouped by address family. Sort by index to match net.Interfaces.
  76. slices.SortFunc(ifaces, func(a, b *iface) int { return int(a.index) - int(b.index) })
  77. out := make([]localInterface, 0, len(ifaces))
  78. for _, i := range ifaces {
  79. addrs := i.addrs
  80. out = append(out, localInterface{
  81. Name: i.name,
  82. Addrs: func() ([]net.Addr, error) { return addrs, nil },
  83. })
  84. }
  85. return out, nil
  86. }
  87. // netlinkAddrDump is syscall.NetlinkRIB(RTM_GETADDR, AF_UNSPEC) without the bind() call. The
  88. // kernel autobinds the socket on the first send, which the sandbox permits.
  89. func netlinkAddrDump() ([]byte, error) {
  90. fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW|unix.SOCK_CLOEXEC, unix.NETLINK_ROUTE)
  91. if err != nil {
  92. return nil, os.NewSyscallError("socket", err)
  93. }
  94. defer unix.Close(fd)
  95. const seq = 1
  96. req := make([]byte, unix.SizeofNlMsghdr+unix.SizeofRtGenmsg)
  97. h := (*unix.NlMsghdr)(unsafe.Pointer(&req[0]))
  98. h.Len = uint32(len(req))
  99. h.Type = unix.RTM_GETADDR
  100. h.Flags = unix.NLM_F_DUMP | unix.NLM_F_REQUEST
  101. h.Seq = seq
  102. req[unix.SizeofNlMsghdr] = unix.AF_UNSPEC // rtgenmsg.rtgen_family
  103. if err := unix.Sendto(fd, req, 0, &unix.SockaddrNetlink{Family: unix.AF_NETLINK}); err != nil {
  104. return nil, os.NewSyscallError("sendto", err)
  105. }
  106. // Read back the port id the kernel assigned on send so replies can be matched.
  107. lsa, err := unix.Getsockname(fd)
  108. if err != nil {
  109. return nil, os.NewSyscallError("getsockname", err)
  110. }
  111. nl, ok := lsa.(*unix.SockaddrNetlink)
  112. if !ok {
  113. return nil, errors.New("netlink socket returned a non-netlink local address")
  114. }
  115. var out []byte
  116. buf := make([]byte, os.Getpagesize())
  117. for {
  118. n, _, err := unix.Recvfrom(fd, buf, 0)
  119. if err != nil {
  120. return nil, os.NewSyscallError("recvfrom", err)
  121. }
  122. if n < unix.NLMSG_HDRLEN {
  123. return nil, fmt.Errorf("short netlink read: %d bytes", n)
  124. }
  125. msgs, err := syscall.ParseNetlinkMessage(buf[:n])
  126. if err != nil {
  127. return nil, err
  128. }
  129. for _, m := range msgs {
  130. if m.Header.Seq != seq || m.Header.Pid != nl.Pid {
  131. return nil, fmt.Errorf("netlink reply for another request: seq %d pid %d", m.Header.Seq, m.Header.Pid)
  132. }
  133. switch m.Header.Type {
  134. case unix.NLMSG_DONE:
  135. return out, nil
  136. case unix.NLMSG_ERROR:
  137. if len(m.Data) < 4 {
  138. return nil, errors.New("short NLMSG_ERROR")
  139. }
  140. errno := *(*int32)(unsafe.Pointer(&m.Data[0]))
  141. return nil, os.NewSyscallError("RTM_GETADDR", unix.Errno(-errno))
  142. }
  143. }
  144. out = append(out, buf[:n]...)
  145. }
  146. }
  147. // interfaceNameByIndex resolves an interface index with SIOCGIFNAME.
  148. func interfaceNameByIndex(fd int, index uint32) (string, error) {
  149. var ifr unix.Ifreq
  150. ifr.SetUint32(index) // ifr_ifindex is in the ifr_ifru union
  151. if err := unix.IoctlIfreq(fd, unix.SIOCGIFNAME, &ifr); err != nil {
  152. return "", err
  153. }
  154. return ifr.Name(), nil
  155. }
  156. // newNetlinkAddr builds the address for one RTM_NEWADDR message. On a point-to-point link
  157. // IFA_LOCAL is our side and IFA_ADDRESS the peer, so IFA_LOCAL wins when both are present.
  158. func newNetlinkAddr(ifam *unix.IfAddrmsg, attrs []syscall.NetlinkRouteAttr) net.Addr {
  159. var pointToPoint bool
  160. for _, a := range attrs {
  161. if a.Attr.Type == unix.IFA_LOCAL {
  162. pointToPoint = true
  163. break
  164. }
  165. }
  166. for _, a := range attrs {
  167. if pointToPoint && a.Attr.Type == unix.IFA_ADDRESS {
  168. continue
  169. }
  170. switch ifam.Family {
  171. case unix.AF_INET:
  172. if a.Attr.Type != unix.IFA_LOCAL && a.Attr.Type != unix.IFA_ADDRESS {
  173. continue
  174. }
  175. if len(a.Value) < net.IPv4len {
  176. return nil
  177. }
  178. return &net.IPNet{
  179. IP: net.IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]),
  180. Mask: net.CIDRMask(int(ifam.Prefixlen), 8*net.IPv4len),
  181. }
  182. case unix.AF_INET6:
  183. if a.Attr.Type != unix.IFA_LOCAL && a.Attr.Type != unix.IFA_ADDRESS {
  184. continue
  185. }
  186. if len(a.Value) < net.IPv6len {
  187. return nil
  188. }
  189. ifa := &net.IPNet{IP: make(net.IP, net.IPv6len), Mask: net.CIDRMask(int(ifam.Prefixlen), 8*net.IPv6len)}
  190. copy(ifa.IP, a.Value[:net.IPv6len])
  191. return ifa
  192. }
  193. }
  194. return nil
  195. }