1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
package testutils
import (
"fmt"
"os"
"runtime"
"testing"
"github.com/jsimonetti/rtnetlink/v2/internal/unix"
"golang.org/x/sync/errgroup"
)
// NetNS returns a file descriptor to a new network namespace.
// The netns handle is automatically closed as part of test cleanup.
func NetNS(tb testing.TB) int {
tb.Helper()
var ns *os.File
var eg errgroup.Group
eg.Go(func() error {
// Lock the new goroutine to its OS thread. Never unlock the goroutine so
// the thread dies when the goroutine ends to avoid having to restore the
// thread's netns.
runtime.LockOSThread()
// Move the current thread to a new network namespace.
if err := unix.Unshare(unix.CLONE_NEWNET); err != nil {
return fmt.Errorf("unsharing netns: %w", err)
}
f, err := os.OpenFile(fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid()),
unix.O_RDONLY|unix.O_CLOEXEC, 0)
if err != nil {
return fmt.Errorf("opening netns handle: %w", err)
}
// Store a namespace reference in the outer scope.
ns = f
return nil
})
if err := eg.Wait(); err != nil {
tb.Fatal(err)
}
tb.Cleanup(func() {
// Maintain a reference to the namespace until the end of the test, where
// the handle will close automatically and the namespace potentially
// disappears if there are no other references (veth/netkit peers, ..) to it.
ns.Close()
})
return int(ns.Fd())
}
|