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:
parent
38173fa22c
commit
694c5ee788
4 changed files with 92 additions and 180 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
Loading…
Add table
Reference in a new issue