netkernelconf_linux.go 3.2 KB

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