netns_linux.go 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. //go:build linux
  2. // +build linux
  3. package netlink
  4. import (
  5. "errors"
  6. "fmt"
  7. "os"
  8. "golang.org/x/sys/unix"
  9. )
  10. // errNetNSDisabled is returned when network namespaces are unavailable on
  11. // a given system.
  12. var errNetNSDisabled = errors.New("netlink: network namespaces are not enabled on this system")
  13. // A netNS is a handle that can manipulate network namespaces.
  14. //
  15. // Operations performed on a netNS must use runtime.LockOSThread before
  16. // manipulating any network namespaces.
  17. type netNS struct {
  18. // The handle to a network namespace.
  19. f *os.File
  20. // Indicates if network namespaces are disabled on this system, and thus
  21. // operations should become a no-op or return errors.
  22. disabled bool
  23. }
  24. // threadNetNS constructs a netNS using the network namespace of the calling
  25. // thread. If the namespace is not the default namespace, runtime.LockOSThread
  26. // should be invoked first.
  27. func threadNetNS() (*netNS, error) {
  28. return fileNetNS(fmt.Sprintf("/proc/self/task/%d/ns/net", unix.Gettid()))
  29. }
  30. // fileNetNS opens file and creates a netNS. fileNetNS should only be called
  31. // directly in tests.
  32. func fileNetNS(file string) (*netNS, error) {
  33. f, err := os.Open(file)
  34. switch {
  35. case err == nil:
  36. return &netNS{f: f}, nil
  37. case os.IsNotExist(err):
  38. // Network namespaces are not enabled on this system. Use this signal
  39. // to return errors elsewhere if the caller explicitly asks for a
  40. // network namespace to be set.
  41. return &netNS{disabled: true}, nil
  42. default:
  43. return nil, err
  44. }
  45. }
  46. // Close releases the handle to a network namespace.
  47. func (n *netNS) Close() error {
  48. return n.do(func() error {
  49. return n.f.Close()
  50. })
  51. }
  52. // FD returns a file descriptor which represents the network namespace.
  53. func (n *netNS) FD() int {
  54. if n.disabled {
  55. // No reasonable file descriptor value in this case, so specify a
  56. // non-existent one.
  57. return -1
  58. }
  59. return int(n.f.Fd())
  60. }
  61. // Restore restores the original network namespace for the calling thread.
  62. func (n *netNS) Restore() error {
  63. return n.do(func() error {
  64. return n.Set(n.FD())
  65. })
  66. }
  67. // Set sets a new network namespace for the current thread using fd.
  68. func (n *netNS) Set(fd int) error {
  69. return n.do(func() error {
  70. return os.NewSyscallError("setns", unix.Setns(fd, unix.CLONE_NEWNET))
  71. })
  72. }
  73. // do runs fn if network namespaces are enabled on this system.
  74. func (n *netNS) do(fn func() error) error {
  75. if n.disabled {
  76. return errNetNSDisabled
  77. }
  78. return fn()
  79. }