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 <mrjana@docker.com>
This commit is contained in:
Jana Radhakrishnan 2015-03-11 23:37:43 +00:00
parent 38173fa22c
commit 694c5ee788
4 changed files with 92 additions and 180 deletions

View file

@ -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
}

View file

@ -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()
}

View file

@ -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)
}

View file

@ -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)
}