netns_linux.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build linux && !android
  4. package netns
  5. import (
  6. "fmt"
  7. "net"
  8. "os"
  9. "sync"
  10. "syscall"
  11. "golang.org/x/sys/unix"
  12. "tailscale.com/envknob"
  13. "tailscale.com/net/interfaces"
  14. "tailscale.com/net/netmon"
  15. "tailscale.com/types/logger"
  16. "tailscale.com/util/linuxfw"
  17. )
  18. // socketMarkWorksOnce is the sync.Once & cached value for useSocketMark.
  19. var socketMarkWorksOnce struct {
  20. sync.Once
  21. v bool
  22. }
  23. // socketMarkWorks returns whether SO_MARK works.
  24. func socketMarkWorks() bool {
  25. addr, err := net.ResolveUDPAddr("udp", "127.0.0.1:1")
  26. if err != nil {
  27. return true // unsure, returning true does the least harm.
  28. }
  29. sConn, err := net.DialUDP("udp", nil, addr)
  30. if err != nil {
  31. return true // unsure, return true
  32. }
  33. defer sConn.Close()
  34. rConn, err := sConn.SyscallConn()
  35. if err != nil {
  36. return true // unsure, return true
  37. }
  38. var sockErr error
  39. err = rConn.Control(func(fd uintptr) {
  40. sockErr = setBypassMark(fd)
  41. })
  42. if err != nil || sockErr != nil {
  43. return false
  44. }
  45. return true
  46. }
  47. var forceBindToDevice = envknob.RegisterBool("TS_FORCE_LINUX_BIND_TO_DEVICE")
  48. // UseSocketMark reports whether SO_MARK is in use.
  49. // If it doesn't, we have to use SO_BINDTODEVICE on our sockets instead.
  50. func UseSocketMark() bool {
  51. if forceBindToDevice() {
  52. return false
  53. }
  54. socketMarkWorksOnce.Do(func() {
  55. socketMarkWorksOnce.v = socketMarkWorks()
  56. })
  57. return socketMarkWorksOnce.v
  58. }
  59. // ignoreErrors returns true if we should ignore setsocketopt errors in
  60. // this instance.
  61. func ignoreErrors() bool {
  62. if os.Getuid() != 0 {
  63. // only root can manipulate these socket flags
  64. return true
  65. }
  66. return false
  67. }
  68. func control(logger.Logf, *netmon.Monitor) func(network, address string, c syscall.RawConn) error {
  69. return controlC
  70. }
  71. // controlC marks c as necessary to dial in a separate network namespace.
  72. //
  73. // It's intentionally the same signature as net.Dialer.Control
  74. // and net.ListenConfig.Control.
  75. func controlC(network, address string, c syscall.RawConn) error {
  76. if isLocalhost(address) {
  77. // Don't bind to an interface for localhost connections.
  78. return nil
  79. }
  80. var sockErr error
  81. err := c.Control(func(fd uintptr) {
  82. if UseSocketMark() {
  83. sockErr = setBypassMark(fd)
  84. } else {
  85. sockErr = bindToDevice(fd)
  86. }
  87. })
  88. if err != nil {
  89. return fmt.Errorf("RawConn.Control on %T: %w", c, err)
  90. }
  91. if sockErr != nil && ignoreErrors() {
  92. // TODO(bradfitz): maybe log once? probably too spammy for e.g. CLI tools like tailscale netcheck.
  93. return nil
  94. }
  95. return sockErr
  96. }
  97. func setBypassMark(fd uintptr) error {
  98. if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_MARK, linuxfw.TailscaleBypassMarkNum); err != nil {
  99. return fmt.Errorf("setting SO_MARK bypass: %w", err)
  100. }
  101. return nil
  102. }
  103. func bindToDevice(fd uintptr) error {
  104. ifc, err := interfaces.DefaultRouteInterface()
  105. if err != nil {
  106. // Make sure we bind to *some* interface,
  107. // or we could get a routing loop.
  108. // "lo" is always wrong, but if we don't have
  109. // a default route anyway, it doesn't matter.
  110. ifc = "lo"
  111. }
  112. if err := unix.SetsockoptString(int(fd), unix.SOL_SOCKET, unix.SO_BINDTODEVICE, ifc); err != nil {
  113. return fmt.Errorf("setting SO_BINDTODEVICE: %w", err)
  114. }
  115. return nil
  116. }