linuxfw.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. // Package linuxfw returns the kind of firewall being used by the kernel.
  4. //go:build linux
  5. package linuxfw
  6. import (
  7. "bytes"
  8. "errors"
  9. "fmt"
  10. "os"
  11. "os/exec"
  12. "strconv"
  13. "strings"
  14. "github.com/tailscale/netlink"
  15. "tailscale.com/types/logger"
  16. )
  17. // MatchDecision is the decision made by the firewall for a packet matched by a rule.
  18. // It is used to decide whether to accept or masquerade a packet in addMatchSubnetRouteMarkRule.
  19. type MatchDecision int
  20. const (
  21. Accept MatchDecision = iota
  22. Masq
  23. )
  24. type FWModeNotSupportedError struct {
  25. Mode FirewallMode
  26. Err error
  27. }
  28. func (e FWModeNotSupportedError) Error() string {
  29. return fmt.Sprintf("firewall mode %q not supported: %v", e.Mode, e.Err)
  30. }
  31. func (e FWModeNotSupportedError) Is(target error) bool {
  32. _, ok := target.(FWModeNotSupportedError)
  33. return ok
  34. }
  35. func (e FWModeNotSupportedError) Unwrap() error {
  36. return e.Err
  37. }
  38. type FirewallMode string
  39. const (
  40. FirewallModeIPTables FirewallMode = "iptables"
  41. FirewallModeNfTables FirewallMode = "nftables"
  42. )
  43. // The following bits are added to packet marks for Tailscale use.
  44. //
  45. // We tried to pick bits sufficiently out of the way that it's
  46. // unlikely to collide with existing uses. We have 4 bytes of mark
  47. // bits to play with. We leave the lower byte alone on the assumption
  48. // that sysadmins would use those. Kubernetes uses a few bits in the
  49. // second byte, so we steer clear of that too.
  50. //
  51. // Empirically, most of the documentation on packet marks on the
  52. // internet gives the impression that the marks are 16 bits
  53. // wide. Based on this, we theorize that the upper two bytes are
  54. // relatively unused in the wild, and so we consume bits 16:23 (the
  55. // third byte).
  56. //
  57. // The constants are in the iptables/iproute2 string format for
  58. // matching and setting the bits, so they can be directly embedded in
  59. // commands.
  60. const (
  61. // The mask for reading/writing the 'firewall mask' bits on a packet.
  62. // See the comment on the const block on why we only use the third byte.
  63. //
  64. // We claim bits 16:23 entirely. For now we only use the lower four
  65. // bits, leaving the higher 4 bits for future use.
  66. TailscaleFwmarkMask = "0xff0000"
  67. TailscaleFwmarkMaskNum = 0xff0000
  68. // Packet is from Tailscale and to a subnet route destination, so
  69. // is allowed to be routed through this machine.
  70. TailscaleSubnetRouteMark = "0x40000"
  71. TailscaleSubnetRouteMarkNum = 0x40000
  72. // Packet was originated by tailscaled itself, and must not be
  73. // routed over the Tailscale network.
  74. TailscaleBypassMark = "0x80000"
  75. TailscaleBypassMarkNum = 0x80000
  76. )
  77. // getTailscaleFwmarkMaskNeg returns the negation of TailscaleFwmarkMask in bytes.
  78. func getTailscaleFwmarkMaskNeg() []byte {
  79. return []byte{0xff, 0x00, 0xff, 0xff}
  80. }
  81. // getTailscaleFwmarkMask returns the TailscaleFwmarkMask in bytes.
  82. func getTailscaleFwmarkMask() []byte {
  83. return []byte{0x00, 0xff, 0x00, 0x00}
  84. }
  85. // getTailscaleSubnetRouteMark returns the TailscaleSubnetRouteMark in bytes.
  86. func getTailscaleSubnetRouteMark() []byte {
  87. return []byte{0x00, 0x04, 0x00, 0x00}
  88. }
  89. // errCode extracts and returns the process exit code from err, or
  90. // zero if err is nil.
  91. func errCode(err error) int {
  92. if err == nil {
  93. return 0
  94. }
  95. var e *exec.ExitError
  96. if ok := errors.As(err, &e); ok {
  97. return e.ExitCode()
  98. }
  99. s := err.Error()
  100. if strings.HasPrefix(s, "exitcode:") {
  101. code, err := strconv.Atoi(s[9:])
  102. if err == nil {
  103. return code
  104. }
  105. }
  106. return -42
  107. }
  108. // checkIPv6 checks whether the system appears to have a working IPv6
  109. // network stack. It returns an error explaining what looks wrong or
  110. // missing. It does not check that IPv6 is currently functional or
  111. // that there's a global address, just that the system would support
  112. // IPv6 if it were on an IPv6 network.
  113. func checkIPv6(logf logger.Logf) error {
  114. _, err := os.Stat("/proc/sys/net/ipv6")
  115. if os.IsNotExist(err) {
  116. return err
  117. }
  118. bs, err := os.ReadFile("/proc/sys/net/ipv6/conf/all/disable_ipv6")
  119. if err != nil {
  120. // Be conservative if we can't find the IPv6 configuration knob.
  121. return err
  122. }
  123. disabled, err := strconv.ParseBool(strings.TrimSpace(string(bs)))
  124. if err != nil {
  125. return errors.New("disable_ipv6 has invalid bool")
  126. }
  127. if disabled {
  128. return errors.New("disable_ipv6 is set")
  129. }
  130. // Older kernels don't support IPv6 policy routing. Some kernels
  131. // support policy routing but don't have this knob, so absence of
  132. // the knob is not fatal.
  133. bs, err = os.ReadFile("/proc/sys/net/ipv6/conf/all/disable_policy")
  134. if err == nil {
  135. disabled, err = strconv.ParseBool(strings.TrimSpace(string(bs)))
  136. if err != nil {
  137. return errors.New("disable_policy has invalid bool")
  138. }
  139. if disabled {
  140. return errors.New("disable_policy is set")
  141. }
  142. }
  143. if err := CheckIPRuleSupportsV6(logf); err != nil {
  144. return fmt.Errorf("kernel doesn't support IPv6 policy routing: %w", err)
  145. }
  146. return nil
  147. }
  148. // checkSupportsV6NAT returns whether the system has a "nat" table in the
  149. // IPv6 netfilter stack.
  150. //
  151. // The nat table was added after the initial release of ipv6
  152. // netfilter, so some older distros ship a kernel that can't NAT IPv6
  153. // traffic.
  154. func checkSupportsV6NAT() bool {
  155. bs, err := os.ReadFile("/proc/net/ip6_tables_names")
  156. if err != nil {
  157. // Can't read the file. Assume SNAT works.
  158. return true
  159. }
  160. if bytes.Contains(bs, []byte("nat\n")) {
  161. return true
  162. }
  163. // In nftables mode, that proc file will be empty. Try another thing:
  164. if exec.Command("modprobe", "ip6table_nat").Run() == nil {
  165. return true
  166. }
  167. return false
  168. }
  169. func CheckIPRuleSupportsV6(logf logger.Logf) error {
  170. // First try just a read-only operation to ideally avoid
  171. // having to modify any state.
  172. if rules, err := netlink.RuleList(netlink.FAMILY_V6); err != nil {
  173. return fmt.Errorf("querying IPv6 policy routing rules: %w", err)
  174. } else {
  175. if len(rules) > 0 {
  176. logf("[v1] kernel supports IPv6 policy routing (found %d rules)", len(rules))
  177. return nil
  178. }
  179. }
  180. // Try to actually create & delete one as a test.
  181. rule := netlink.NewRule()
  182. rule.Priority = 1234
  183. rule.Mark = TailscaleBypassMarkNum
  184. rule.Table = 52
  185. rule.Family = netlink.FAMILY_V6
  186. // First delete the rule unconditionally, and don't check for
  187. // errors. This is just cleaning up anything that might be already
  188. // there.
  189. netlink.RuleDel(rule)
  190. // And clean up on exit.
  191. defer netlink.RuleDel(rule)
  192. return netlink.RuleAdd(rule)
  193. }