1
0

localaddrs_android.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. //go:build android
  2. package nicfinder
  3. import (
  4. "context"
  5. "errors"
  6. "fmt"
  7. "log/slog"
  8. "net/netip"
  9. "os"
  10. "github.com/DefinedNet/netlink"
  11. "github.com/DefinedNet/netlink/nl"
  12. "golang.org/x/sys/unix"
  13. )
  14. // Android denies bind() on netlink route sockets and RTM_GETLINK, so net.Interfaces fails with EACCES.
  15. // Enumerate with an RTM_GETADDR dump on an unbound socket instead.
  16. // localInterfaces returns every interface that has an address, with its addresses. It lists
  17. // addresses over a netlink socket that is never bound and names each index with SIOCGIFNAME, the
  18. // same operations Bionic's getifaddrs uses for app UIDs, which the sandbox permits.
  19. func localAddrs(ctx context.Context, l *slog.Logger, filter Filter) ([]netip.Addr, error) {
  20. var errs []error
  21. addrs, err := netlinkAddrList()
  22. if err != nil {
  23. if !errors.Is(err, netlink.ErrDumpInterrupted) {
  24. return nil, fmt.Errorf("failed to enumerate local interfaces: %w", err)
  25. }
  26. // The address table kept changing under the dump and netlink gave up retrying. What it
  27. // returned is still worth advertising.
  28. errs = append(errs, fmt.Errorf("local addresses may be incomplete: %w", 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. var out []netip.Addr
  37. nameAllowed := make(map[int]bool) // by interface index, once named
  38. for _, a := range addrs {
  39. allow, named := nameAllowed[a.LinkIndex]
  40. if (named && !allow) || a.IPNet == nil {
  41. continue
  42. }
  43. addr, ok := allowedAddr(ctx, l, filter, a.IPNet)
  44. if !ok {
  45. continue
  46. }
  47. if !named {
  48. name, err := interfaceNameByIndex(sfd, a.LinkIndex)
  49. // ENODEV means the interface went away after the dump. Any other failure is reported
  50. // and the interface skipped, so the rest are still returned.
  51. if err != nil && !errors.Is(err, unix.ENODEV) {
  52. errs = append(errs, fmt.Errorf("failed to resolve name of interface %d: %w", a.LinkIndex, err))
  53. }
  54. allow = err == nil && allowName(ctx, l, filter, name)
  55. nameAllowed[a.LinkIndex] = allow
  56. }
  57. if allow {
  58. out = append(out, addr)
  59. }
  60. }
  61. return out, errors.Join(errs...)
  62. }
  63. // netlinkAddrList is netlink.AddrList over a socket that is never bound. The kernel autobinds it
  64. // on the first send, which the sandbox permits.
  65. func netlinkAddrList() ([]netlink.Addr, error) {
  66. fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW|unix.SOCK_CLOEXEC, unix.NETLINK_ROUTE)
  67. if err != nil {
  68. return nil, os.NewSyscallError("socket", err)
  69. }
  70. s, err := nl.NewNetlinkSocketFromFd(fd)
  71. if err != nil {
  72. unix.Close(fd)
  73. return nil, err
  74. }
  75. h := netlink.NewHandleFromSockets(
  76. map[int]*nl.SocketHandle{unix.NETLINK_ROUTE: {Socket: s}},
  77. netlink.HandleOptions{RetryInterrupted: true},
  78. )
  79. defer h.Close()
  80. if err := h.SetSocketTimeout(netlink.GetSocketTimeout()); err != nil {
  81. return nil, err
  82. }
  83. return h.AddrList(nil, netlink.FAMILY_ALL)
  84. }
  85. // interfaceNameByIndex resolves an interface index with SIOCGIFNAME.
  86. func interfaceNameByIndex(fd int, index int) (string, error) {
  87. var ifr unix.Ifreq
  88. ifr.SetUint32(uint32(index)) // ifr_ifindex is in the ifr_ifru union
  89. if err := unix.IoctlIfreq(fd, unix.SIOCGIFNAME, &ifr); err != nil {
  90. return "", err
  91. }
  92. return ifr.Name(), nil
  93. }