linuxfw.go 5.4 KB

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