netns_linux.go 2.4 KB

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