interfaces_darwin.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package netmon
  4. import (
  5. "fmt"
  6. "net"
  7. "strings"
  8. "syscall"
  9. "unsafe"
  10. "golang.org/x/net/route"
  11. "golang.org/x/sys/unix"
  12. "tailscale.com/syncs"
  13. "tailscale.com/util/mak"
  14. )
  15. // fetchRoutingTable calls route.FetchRIB, fetching NET_RT_DUMP2.
  16. func fetchRoutingTable() (rib []byte, err error) {
  17. return route.FetchRIB(syscall.AF_UNSPEC, syscall.NET_RT_DUMP2, 0)
  18. }
  19. func parseRoutingTable(rib []byte) ([]route.Message, error) {
  20. return route.ParseRIB(syscall.NET_RT_IFLIST2, rib)
  21. }
  22. var ifNames struct {
  23. syncs.Mutex
  24. m map[int]string // ifindex => name
  25. }
  26. func init() {
  27. interfaceDebugExtras = interfaceDebugExtrasDarwin
  28. }
  29. // getDelegatedInterface returns the interface index of the underlying interface
  30. // for the given interface index. 0 is returned if the interface does not
  31. // delegate.
  32. func getDelegatedInterface(ifIndex int) (int, error) {
  33. ifNames.Lock()
  34. defer ifNames.Unlock()
  35. // To get the delegated interface, we do what ifconfig does and use the
  36. // SIOCGIFDELEGATE ioctl. It operates in term of a ifreq struct, which
  37. // has to be populated with a interface name. To avoid having to do a
  38. // interface index -> name lookup every time, we cache interface names
  39. // (since indexes and names are stable after boot).
  40. ifName, ok := ifNames.m[ifIndex]
  41. if !ok {
  42. iface, err := net.InterfaceByIndex(ifIndex)
  43. if err != nil {
  44. return 0, err
  45. }
  46. ifName = iface.Name
  47. mak.Set(&ifNames.m, ifIndex, ifName)
  48. }
  49. // Only tunnels (like Tailscale itself) have a delegated interface, avoid
  50. // the ioctl if we can.
  51. if !strings.HasPrefix(ifName, "utun") {
  52. return 0, nil
  53. }
  54. // We don't cache the result of the ioctl, since the delegated interface can
  55. // change, e.g. if the user changes the preferred service order in the
  56. // network preference pane.
  57. fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
  58. if err != nil {
  59. return 0, err
  60. }
  61. defer unix.Close(fd)
  62. // Match the ifreq struct/union from the bsd/net/if.h header in the Darwin
  63. // open source release.
  64. var ifr struct {
  65. ifr_name [unix.IFNAMSIZ]byte
  66. ifr_delegated uint32
  67. }
  68. copy(ifr.ifr_name[:], ifName)
  69. // SIOCGIFDELEGATE is not in the Go x/sys package or in the public macOS
  70. // <sys/sockio.h> headers. However, it is in the Darwin/xnu open source
  71. // release (and is used by ifconfig, see
  72. // https://github.com/apple-oss-distributions/network_cmds/blob/6ccdc225ad5aa0d23ea5e7d374956245d2462427/ifconfig.tproj/ifconfig.c#L2183-L2187).
  73. // We generate its value by evaluating the `_IOWR('i', 157, struct ifreq)`
  74. // macro, which is how it's defined in
  75. // https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/sys/sockio.h#L264
  76. const SIOCGIFDELEGATE = 0xc020699d
  77. _, _, errno := syscall.Syscall(
  78. syscall.SYS_IOCTL,
  79. uintptr(fd),
  80. uintptr(SIOCGIFDELEGATE),
  81. uintptr(unsafe.Pointer(&ifr)))
  82. if errno != 0 {
  83. return 0, errno
  84. }
  85. return int(ifr.ifr_delegated), nil
  86. }
  87. func interfaceDebugExtrasDarwin(ifIndex int) (string, error) {
  88. delegated, err := getDelegatedInterface(ifIndex)
  89. if err != nil {
  90. return "", err
  91. }
  92. if delegated == 0 {
  93. return "", nil
  94. }
  95. return fmt.Sprintf("delegated=%d", delegated), nil
  96. }