kernel_tun_linux.go 1.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. package xray
  2. import (
  3. "errors"
  4. "io/fs"
  5. "os"
  6. "sync"
  7. "golang.org/x/sys/unix"
  8. )
  9. // The files the core's WireGuard kernel TUN opens before it creates the device
  10. // (proxy/wireguard/tun_linux.go); a missing one is skipped there too.
  11. var kernelTunSysctls = []string{
  12. "/proc/sys/net/ipv4/conf/all/rp_filter",
  13. "/proc/sys/net/ipv6/conf/all/disable_ipv6",
  14. "/proc/sys/net/ipv6/conf/all/rp_filter",
  15. }
  16. // KernelTunBlocked reports that the core would put a WireGuard outbound on a kernel TUN
  17. // (it holds CAP_NET_ADMIN) where a read-only /proc/sys or a missing /dev/net/tun aborts its start.
  18. var KernelTunBlocked = sync.OnceValue(func() bool {
  19. return hasEffectiveNetAdmin() && !kernelTunReachable()
  20. })
  21. func hasEffectiveNetAdmin() bool {
  22. hdr := unix.CapUserHeader{Version: unix.LINUX_CAPABILITY_VERSION_3}
  23. var data [2]unix.CapUserData
  24. if err := unix.Capget(&hdr, &data[0]); err != nil {
  25. return false
  26. }
  27. return data[0].Effective&(1<<unix.CAP_NET_ADMIN) != 0
  28. }
  29. func kernelTunReachable() bool {
  30. for _, path := range kernelTunSysctls {
  31. f, err := os.OpenFile(path, os.O_WRONLY, 0)
  32. if errors.Is(err, fs.ErrNotExist) {
  33. continue
  34. }
  35. if err != nil {
  36. return false
  37. }
  38. _ = f.Close()
  39. }
  40. tun, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0)
  41. if err != nil {
  42. return false
  43. }
  44. _ = tun.Close()
  45. return true
  46. }