From 694c5ee788411979407d6b44c98385da3bd554b4 Mon Sep 17 00:00:00 2001 From: Jana Radhakrishnan Date: Wed, 11 Mar 2015 23:37:43 +0000 Subject: [PATCH] Reimplement create namespace and move interafec to ns without reexec This removes the dependency on reexec so that libnetwork can be used as a library. Signed-off-by: Jana Radhakrishnan --- libnetwork/namespace.go | 97 +++++++++++++++++++++++++++-- libnetwork/reexec.go | 45 ------------- libnetwork/reexec_move_interface.go | 78 ----------------------- libnetwork/reexec_netns_create.go | 52 ---------------- 4 files changed, 92 insertions(+), 180 deletions(-) delete mode 100644 libnetwork/reexec.go delete mode 100644 libnetwork/reexec_move_interface.go delete mode 100644 libnetwork/reexec_netns_create.go diff --git a/libnetwork/namespace.go b/libnetwork/namespace.go index 56dc20086b..4c4c9ac975 100644 --- a/libnetwork/namespace.go +++ b/libnetwork/namespace.go @@ -1,6 +1,14 @@ package libnetwork -import "syscall" +import ( + "fmt" + "os" + "runtime" + "syscall" + + "github.com/vishvananda/netlink" + "github.com/vishvananda/netns" +) // The networkNamespace type is the default implementation of the Namespace // interface. It simply creates a new network namespace, and moves an interface @@ -11,17 +19,96 @@ type networkNamespace struct { } func createNetworkNamespace(path string) (Namespace, error) { - if err := reexec(cmdReexecCreateNamespace, path); err != nil { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + origns, err := netns.Get() + if err != nil { return nil, err } + defer origns.Close() + + if err := createNamespaceFile(path); err != nil { + return nil, err + } + + 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 + } + + if err := syscall.Mount("/proc/self/ns/net", path, "bind", syscall.MS_BIND, ""); err != nil { + return nil, err + } + return &networkNamespace{path: path}, nil } -func (n *networkNamespace) AddInterface(i *Interface) error { - // TODO Open pipe, pass fd to child and write serialized Interface on it. - if err := reexec(cmdReexecMoveInterface, i.SrcName, i.DstName); err != nil { +func createNamespaceFile(path string) (err error) { + var f *os.File + if f, err = os.Create(path); err == nil { + f.Close() + } + return err +} + +func loopbackUp() error { + iface, err := netlink.LinkByName("lo") + if err != nil { return err } + return netlink.LinkSetUp(iface) +} + +func (n *networkNamespace) AddInterface(i *Interface) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + origns, err := netns.Get() + if err != nil { + return err + } + defer origns.Close() + + f, err := os.OpenFile(n.path, os.O_RDONLY, 0) + if err != nil { + return fmt.Errorf("failed get network namespace %q: %v", n.path, err) + } + defer f.Close() + + // Find the network inteerface identified by the SrcName attribute. + iface, err := netlink.LinkByName(i.SrcName) + if err != nil { + return err + } + + // Move the network interface to the destination namespace. + nsFD := f.Fd() + if err := netlink.LinkSetNsFd(iface, int(nsFD)); err != nil { + return err + } + + if err = netns.Set(netns.NsHandle(nsFD)); err != nil { + return err + } + defer netns.Set(origns) + + // Configure the interface now this is moved in the proper namespace. + if err := configureInterface(iface, i); err != nil { + return err + } + + // Up the interface. + if err := netlink.LinkSetUp(iface); err != nil { + return err + } + n.interfaces = append(n.interfaces, i) return nil } diff --git a/libnetwork/reexec.go b/libnetwork/reexec.go deleted file mode 100644 index 4f9ab6bf70..0000000000 --- a/libnetwork/reexec.go +++ /dev/null @@ -1,45 +0,0 @@ -package libnetwork - -import ( - "fmt" - "os" - "os/exec" - - dre "github.com/docker/docker/pkg/reexec" -) - -type reexecCommand int - -const ( - cmdReexecCreateNamespace reexecCommand = iota - cmdReexecMoveInterface -) - -var reexecCommands = map[reexecCommand]struct { - Key string - Entrypoint func() -}{ - cmdReexecCreateNamespace: {"netns-create", reexecCreateNamespace}, - cmdReexecMoveInterface: {"netns-moveif", reexecMoveInterface}, -} - -func init() { - for _, reexecCmd := range reexecCommands { - dre.Register(reexecCmd.Key, reexecCmd.Entrypoint) - } -} - -func reexec(command reexecCommand, params ...string) error { - reexecCommand, ok := reexecCommands[command] - if !ok { - return fmt.Errorf("unknown reexec command %q", command) - } - - cmd := &exec.Cmd{ - Path: dre.Self(), - Args: append([]string{reexecCommand.Key}, params...), - Stdout: os.Stdout, - Stderr: os.Stderr, - } - return cmd.Run() -} diff --git a/libnetwork/reexec_move_interface.go b/libnetwork/reexec_move_interface.go deleted file mode 100644 index bdb46fc482..0000000000 --- a/libnetwork/reexec_move_interface.go +++ /dev/null @@ -1,78 +0,0 @@ -package libnetwork - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "runtime" - "syscall" - - "github.com/vishvananda/netlink" -) - -type setupError struct { - Message string -} - -func (s setupError) Error() string { - return s.Message -} - -func reexecMoveInterface() { - runtime.LockOSThread() - - var ( - err error - pipe = os.NewFile(3, "child") - ) - - defer func() { - if err != nil { - ioutil.ReadAll(pipe) - if err := json.NewEncoder(pipe).Encode(setupError{Message: err.Error()}); err != nil { - panic(err) - } - } - pipe.Close() - }() - - n := &Interface{} - if err = json.NewDecoder(pipe).Decode(n); err == nil { - err = setupInNS(os.Args[1], n) - } -} - -func setupInNS(nsPath string, settings *Interface) error { - f, err := os.OpenFile(nsPath, os.O_RDONLY, 0) - if err != nil { - return fmt.Errorf("failed get network namespace %q: %v", nsPath, err) - } - - // Find the network inteerface identified by the SrcName attribute. - iface, err := netlink.LinkByName(settings.SrcName) - if err != nil { - return err - } - - // Move the network interface to the destination namespace. - nsFD := f.Fd() - if err := netlink.LinkSetNsFd(iface, int(nsFD)); err != nil { - return err - } - f.Close() - - // Move the executing code to the destination namespace so we can start - // configure the interface. - if err := setns(nsFD, syscall.CLONE_NEWNET); err != nil { - return err - } - - // Configure the interface now this is moved in the proper namespace. - if err := configureInterface(iface, settings); err != nil { - return err - } - - // Up the interface. - return netlink.LinkSetUp(iface) -} diff --git a/libnetwork/reexec_netns_create.go b/libnetwork/reexec_netns_create.go deleted file mode 100644 index 827669f5b7..0000000000 --- a/libnetwork/reexec_netns_create.go +++ /dev/null @@ -1,52 +0,0 @@ -package libnetwork - -import ( - "os" - "runtime" - "syscall" - - log "github.com/Sirupsen/logrus" - "github.com/vishvananda/netlink" -) - -func reexecCreateNamespace() { - runtime.LockOSThread() - - if len(os.Args) < 2 { - log.Fatalf("no namespace path provided") - } - - if err := createNamespaceFile(os.Args[1]); err != nil { - log.Fatal(err) - } - - if err := syscall.Unshare(syscall.CLONE_NEWNET); err != nil { - log.Fatal(err) - } - - if err := loopbackUp(); err != nil { - log.Fatal(err) - } - - if err := syscall.Mount("/proc/self/ns/net", os.Args[1], "bind", syscall.MS_BIND, ""); err != nil { - log.Fatal(err) - } - - os.Exit(0) -} - -func createNamespaceFile(path string) (err error) { - var f *os.File - if f, err = os.Create(path); err == nil { - f.Close() - } - return err -} - -func loopbackUp() error { - iface, err := netlink.LinkByName("lo") - if err != nil { - return err - } - return netlink.LinkSetUp(iface) -}