|
@@ -4,17 +4,27 @@ import (
|
|
"fmt"
|
|
"fmt"
|
|
"net"
|
|
"net"
|
|
"os"
|
|
"os"
|
|
|
|
+ "os/exec"
|
|
"runtime"
|
|
"runtime"
|
|
"sync"
|
|
"sync"
|
|
"syscall"
|
|
"syscall"
|
|
|
|
+ "time"
|
|
|
|
|
|
|
|
+ log "github.com/Sirupsen/logrus"
|
|
|
|
+ "github.com/docker/docker/pkg/reexec"
|
|
"github.com/vishvananda/netlink"
|
|
"github.com/vishvananda/netlink"
|
|
"github.com/vishvananda/netns"
|
|
"github.com/vishvananda/netns"
|
|
)
|
|
)
|
|
|
|
|
|
const prefix = "/var/run/docker/netns"
|
|
const prefix = "/var/run/docker/netns"
|
|
|
|
|
|
-var once sync.Once
|
|
|
|
|
|
+var (
|
|
|
|
+ once sync.Once
|
|
|
|
+ garbagePathMap = make(map[string]bool)
|
|
|
|
+ gpmLock sync.Mutex
|
|
|
|
+ gpmWg sync.WaitGroup
|
|
|
|
+ gpmCleanupPeriod = 60 * time.Second
|
|
|
|
+)
|
|
|
|
|
|
// The networkNamespace type is the linux implementation of the Sandbox
|
|
// The networkNamespace type is the linux implementation of the Sandbox
|
|
// interface. It represents a linux network namespace, and moves an interface
|
|
// interface. It represents a linux network namespace, and moves an interface
|
|
@@ -26,11 +36,49 @@ type networkNamespace struct {
|
|
sync.Mutex
|
|
sync.Mutex
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+func init() {
|
|
|
|
+ reexec.Register("netns-create", reexecCreateNamespace)
|
|
|
|
+}
|
|
|
|
+
|
|
func createBasePath() {
|
|
func createBasePath() {
|
|
err := os.MkdirAll(prefix, 0644)
|
|
err := os.MkdirAll(prefix, 0644)
|
|
if err != nil && !os.IsExist(err) {
|
|
if err != nil && !os.IsExist(err) {
|
|
panic("Could not create net namespace path directory")
|
|
panic("Could not create net namespace path directory")
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ // Start the garbage collection go routine
|
|
|
|
+ go removeUnusedPaths()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func removeUnusedPaths() {
|
|
|
|
+ for range time.Tick(gpmCleanupPeriod) {
|
|
|
|
+ gpmLock.Lock()
|
|
|
|
+ pathList := make([]string, 0, len(garbagePathMap))
|
|
|
|
+ for path := range garbagePathMap {
|
|
|
|
+ pathList = append(pathList, path)
|
|
|
|
+ }
|
|
|
|
+ garbagePathMap = make(map[string]bool)
|
|
|
|
+ gpmWg.Add(1)
|
|
|
|
+ gpmLock.Unlock()
|
|
|
|
+
|
|
|
|
+ for _, path := range pathList {
|
|
|
|
+ os.Remove(path)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ gpmWg.Done()
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func addToGarbagePaths(path string) {
|
|
|
|
+ gpmLock.Lock()
|
|
|
|
+ garbagePathMap[path] = true
|
|
|
|
+ gpmLock.Unlock()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func removeFromGarbagePaths(path string) {
|
|
|
|
+ gpmLock.Lock()
|
|
|
|
+ delete(garbagePathMap, path)
|
|
|
|
+ gpmLock.Unlock()
|
|
}
|
|
}
|
|
|
|
|
|
// GenerateKey generates a sandbox key based on the passed
|
|
// GenerateKey generates a sandbox key based on the passed
|
|
@@ -55,6 +103,20 @@ func NewSandbox(key string, osCreate bool) (Sandbox, error) {
|
|
return &networkNamespace{path: key, sinfo: info}, nil
|
|
return &networkNamespace{path: key, sinfo: info}, nil
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+func reexecCreateNamespace() {
|
|
|
|
+ if len(os.Args) < 2 {
|
|
|
|
+ log.Fatal("no namespace path provided")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if err := syscall.Mount("/proc/self/ns/net", os.Args[1], "bind", syscall.MS_BIND, ""); err != nil {
|
|
|
|
+ log.Fatal(err)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if err := loopbackUp(); err != nil {
|
|
|
|
+ log.Fatal(err)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
|
|
func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
|
|
runtime.LockOSThread()
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
@@ -69,23 +131,18 @@ func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ cmd := &exec.Cmd{
|
|
|
|
+ Path: reexec.Self(),
|
|
|
|
+ Args: append([]string{"netns-create"}, path),
|
|
|
|
+ Stdout: os.Stdout,
|
|
|
|
+ Stderr: os.Stderr,
|
|
|
|
+ }
|
|
if osCreate {
|
|
if osCreate {
|
|
- defer netns.Set(origns)
|
|
|
|
- newns, err := netns.New()
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- defer newns.Close()
|
|
|
|
-
|
|
|
|
- if err := loopbackUp(); err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
|
|
+ cmd.SysProcAttr = &syscall.SysProcAttr{}
|
|
|
|
+ cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNET
|
|
}
|
|
}
|
|
-
|
|
|
|
- procNet := fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), syscall.Gettid())
|
|
|
|
-
|
|
|
|
- if err := syscall.Mount(procNet, path, "bind", syscall.MS_BIND, ""); err != nil {
|
|
|
|
- return nil, err
|
|
|
|
|
|
+ if err := cmd.Run(); err != nil {
|
|
|
|
+ return nil, fmt.Errorf("namespace creation reexec command failed: %v", err)
|
|
}
|
|
}
|
|
|
|
|
|
interfaces := []*Interface{}
|
|
interfaces := []*Interface{}
|
|
@@ -93,10 +150,9 @@ func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
|
|
return info, nil
|
|
return info, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func cleanupNamespaceFile(path string) {
|
|
|
|
|
|
+func unmountNamespaceFile(path string) {
|
|
if _, err := os.Stat(path); err == nil {
|
|
if _, err := os.Stat(path); err == nil {
|
|
- n := &networkNamespace{path: path}
|
|
|
|
- n.Destroy()
|
|
|
|
|
|
+ syscall.Unmount(path, syscall.MNT_DETACH)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -104,11 +160,20 @@ func createNamespaceFile(path string) (err error) {
|
|
var f *os.File
|
|
var f *os.File
|
|
|
|
|
|
once.Do(createBasePath)
|
|
once.Do(createBasePath)
|
|
- // cleanup namespace file if it already exists because of a previous ungraceful exit.
|
|
|
|
- cleanupNamespaceFile(path)
|
|
|
|
|
|
+ // Remove it from garbage collection list if present
|
|
|
|
+ removeFromGarbagePaths(path)
|
|
|
|
+
|
|
|
|
+ // If the path is there unmount it first
|
|
|
|
+ unmountNamespaceFile(path)
|
|
|
|
+
|
|
|
|
+ // wait for garbage collection to complete if it is in progress
|
|
|
|
+ // before trying to create the file.
|
|
|
|
+ gpmWg.Wait()
|
|
|
|
+
|
|
if f, err = os.Create(path); err == nil {
|
|
if f, err = os.Create(path); err == nil {
|
|
f.Close()
|
|
f.Close()
|
|
}
|
|
}
|
|
|
|
+
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
|
|
|
|
@@ -269,5 +334,7 @@ func (n *networkNamespace) Destroy() error {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
|
|
|
|
- return os.Remove(n.path)
|
|
|
|
|
|
+ // Stash it into the garbage collection list
|
|
|
|
+ addToGarbagePaths(n.path)
|
|
|
|
+ return nil
|
|
}
|
|
}
|