netkernelconf_linux.go 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package netkernelconf
  4. import (
  5. "fmt"
  6. "github.com/safchain/ethtool"
  7. )
  8. const (
  9. rxWantFeature = "rx-udp-gro-forwarding"
  10. rxDoNotWantFeature = "rx-gro-list"
  11. txFeature = "tx-udp-segmentation"
  12. )
  13. // CheckUDPGROForwarding checks if the machine is optimally configured to
  14. // forward UDP packets between the default route and Tailscale TUN interfaces.
  15. // It returns a non-nil warn in the case that the configuration is suboptimal.
  16. // It returns a non-nil err in the case that an error is encountered while
  17. // performing the check.
  18. func CheckUDPGROForwarding(tunInterface, defaultRouteInterface string) (warn, err error) {
  19. const kbLink = "\nSee https://tailscale.com/s/ethtool-config-udp-gro"
  20. errWithPrefix := func(format string, a ...any) error {
  21. const errPrefix = "couldn't check system's UDP GRO forwarding configuration, "
  22. return fmt.Errorf(errPrefix+format, a...)
  23. }
  24. e, err := ethtool.NewEthtool()
  25. if err != nil {
  26. return nil, errWithPrefix("failed to init ethtool: %v", err)
  27. }
  28. defer e.Close()
  29. tunFeatures, err := e.Features(tunInterface)
  30. if err != nil {
  31. return nil, errWithPrefix("failed to retrieve TUN device features: %v", err)
  32. }
  33. if !tunFeatures[txFeature] {
  34. // if txFeature is disabled/nonexistent on the TUN then UDP GRO
  35. // forwarding doesn't matter, we won't be taking advantage of it.
  36. return nil, nil
  37. }
  38. defaultFeatures, err := e.Features(defaultRouteInterface)
  39. if err != nil {
  40. return nil, errWithPrefix("failed to retrieve default route interface features: %v", err)
  41. }
  42. defaultHasRxWant, ok := defaultFeatures[rxWantFeature]
  43. if !ok {
  44. // unlikely the feature is nonexistant with txFeature in the TUN driver
  45. // being added to the kernel later than rxWantFeature, but let's be sure
  46. return nil, nil
  47. }
  48. if !defaultHasRxWant || defaultFeatures[rxDoNotWantFeature] {
  49. return fmt.Errorf("UDP GRO forwarding is suboptimally configured on %s, UDP forwarding throughput capability will increase with a configuration change.%s", defaultRouteInterface, kbLink), nil
  50. }
  51. return nil, nil
  52. }
  53. // SetUDPGROForwarding enables UDP GRO forwarding for the provided default
  54. // interface. It validates if the provided tun interface has UDP segmentation
  55. // enabled and, if not, returns an error. See
  56. // https://tailscale.com/kb/1320/performance-best-practices#linux-optimizations-for-subnet-routers-and-exit-nodes
  57. func SetUDPGROForwarding(tunInterface, defaultInterface string) error {
  58. e, err := ethtool.NewEthtool()
  59. if err != nil {
  60. return fmt.Errorf("failed to init ethtool: %w", err)
  61. }
  62. defer e.Close()
  63. tunFeatures, err := e.Features(tunInterface)
  64. if err != nil {
  65. return fmt.Errorf("failed to retrieve TUN device features: %w", err)
  66. }
  67. if !tunFeatures[txFeature] {
  68. // if txFeature is disabled/nonexistent on the TUN then UDP GRO
  69. // forwarding doesn't matter, we won't be taking advantage of it.
  70. return fmt.Errorf("Not enabling UDP GRO forwarding as UDP segmentation is disabled for Tailscale interface")
  71. }
  72. if err := e.Change(defaultInterface, map[string]bool{rxWantFeature: true, rxDoNotWantFeature: false}); err != nil {
  73. return fmt.Errorf("error enabling UDP GRO forwarding: %w", err)
  74. }
  75. return nil
  76. }