Переглянути джерело

Initial import

Signed-off-by: Arnaud Porterie <arnaud.porterie@docker.com>
Arnaud Porterie 10 роки тому
батько
коміт
0d29ca540f

+ 37 - 0
libnetwork/bridge/bridge.go

@@ -0,0 +1,37 @@
+package bridge
+
+import (
+	"net"
+
+	"github.com/docker/libnetwork"
+)
+
+const networkType = "bridgednetwork"
+
+func init() {
+	libnetwork.RegisterNetworkType(networkType, Create)
+}
+
+func Create(options libnetwork.strategyParams) libnetwork.Network {
+	return &bridgeNetwork{}
+}
+
+type Configuration struct {
+	Subnet net.IPNet
+}
+
+type bridgeNetwork struct {
+	Config Configuration
+}
+
+func (b *bridgeNetwork) Name() string {
+	return b.Id
+}
+
+func (b *bridgeNetwork) Type() string {
+	return networkType
+}
+
+func (b *bridgeNetwork) Link(name string) ([]*libnetwork.Interface, error) {
+	return nil, nil
+}

+ 95 - 0
libnetwork/configure.go

@@ -0,0 +1,95 @@
+package libnetwork
+
+import (
+	"fmt"
+	"net"
+
+	"github.com/vishvananda/netlink"
+)
+
+func configureInterface(iface netlink.Link, settings *Interface) error {
+	ifaceName := iface.Attrs().Name
+	ifaceConfigurators := []struct {
+		Fn         func(netlink.Link, *Interface) error
+		ErrMessage string
+	}{
+		{setInterfaceName, fmt.Sprintf("error renaming interface %q to %q", ifaceName, settings.DstName)},
+		{setInterfaceMAC, fmt.Sprintf("error setting interface %q MAC address to %q", ifaceName, settings.MacAddress)},
+		{setInterfaceIP, fmt.Sprintf("error setting interface %q IP to %q", ifaceName, settings.Address)},
+		{setInterfaceIPv6, fmt.Sprintf("error setting interface %q IPv6 to %q", ifaceName, settings.AddressIPv6)},
+		{setInterfaceMTU, fmt.Sprintf("error setting interface %q MTU to %q", ifaceName, settings.MTU)},
+		{setInterfaceGateway, fmt.Sprintf("error setting interface %q gateway to %q", ifaceName, settings.Gateway)},
+		{setInterfaceGatewayIPv6, fmt.Sprintf("error setting interface %q IPv6 gateway to %q", ifaceName, settings.GatewayIPv6)},
+	}
+
+	for _, config := range ifaceConfigurators {
+		if err := config.Fn(iface, settings); err != nil {
+			return fmt.Errorf("%s: %v", config.ErrMessage, err)
+		}
+	}
+	return nil
+}
+
+func setGatewayIP(iface netlink.Link, ip net.IP) error {
+	return netlink.RouteAdd(&netlink.Route{
+		LinkIndex: iface.Attrs().Index,
+		Scope:     netlink.SCOPE_UNIVERSE,
+		Gw:        ip,
+	})
+}
+
+func setInterfaceGateway(iface netlink.Link, settings *Interface) error {
+	ip := net.ParseIP(settings.Gateway)
+	if ip == nil {
+		return fmt.Errorf("bad address format %q", settings.Gateway)
+	}
+	return setGatewayIP(iface, ip)
+}
+
+func setInterfaceGatewayIPv6(iface netlink.Link, settings *Interface) error {
+	if settings.GatewayIPv6 != "" {
+		return nil
+	}
+
+	ip := net.ParseIP(settings.GatewayIPv6)
+	if ip == nil {
+		return fmt.Errorf("bad address format %q", settings.GatewayIPv6)
+	}
+	return setGatewayIP(iface, ip)
+}
+
+func setInterfaceIP(iface netlink.Link, settings *Interface) (err error) {
+	var ipAddr *netlink.Addr
+	if ipAddr, err = netlink.ParseAddr(settings.Address); err == nil {
+		err = netlink.AddrAdd(iface, ipAddr)
+	}
+	return err
+}
+
+func setInterfaceIPv6(iface netlink.Link, settings *Interface) (err error) {
+	if settings.AddressIPv6 != "" {
+		return nil
+	}
+
+	var ipAddr *netlink.Addr
+	if ipAddr, err = netlink.ParseAddr(settings.AddressIPv6); err == nil {
+		err = netlink.AddrAdd(iface, ipAddr)
+	}
+	return err
+}
+
+func setInterfaceMAC(iface netlink.Link, settings *Interface) (err error) {
+	var hwAddr net.HardwareAddr
+	if hwAddr, err = net.ParseMAC(settings.MacAddress); err == nil {
+		err = netlink.LinkSetHardwareAddr(iface, hwAddr)
+	}
+	return err
+}
+
+func setInterfaceMTU(iface netlink.Link, settings *Interface) error {
+	return netlink.LinkSetMTU(iface, settings.MTU)
+}
+
+func setInterfaceName(iface netlink.Link, settings *Interface) error {
+	return netlink.LinkSetName(iface, settings.DstName)
+}

+ 30 - 0
libnetwork/namespace.go

@@ -0,0 +1,30 @@
+package libnetwork
+
+type networkNamespace struct {
+	path       string
+	interfaces []*Interface
+}
+
+// Create a new network namespace mounted on the provided path.
+func NewNamespace(path string) (Namespace, error) {
+	if err := Reexec(ReexecCreateNamespace, path); err != nil {
+		return nil, err
+	}
+	return &networkNamespace{path: path}, nil
+}
+
+func (n *networkNamespace) AddInterface(i *Interface) error {
+	if err := Reexec(ReexecMoveInterface, i.SrcName, i.DstName); err != nil {
+		return err
+	}
+	n.interfaces = append(n.interfaces, i)
+	return nil
+}
+
+func (n *networkNamespace) Interfaces() []*Interface {
+	return n.interfaces
+}
+
+func (n *networkNamespace) Path() string {
+	return n.path
+}

+ 69 - 0
libnetwork/network.go

@@ -0,0 +1,69 @@
+// Package libnetwork provides basic fonctionalities and extension points to
+// create network namespaces and allocate interfaces for containers to use.
+//
+//    // Create a network for containers to join.
+//    network, err := libnetwork.NewNetwork("simplebridge", &Options{})
+//    if err != nil {
+//    	return err
+//    }
+//
+//    // For a new container: create network namespace (providing the path).
+//    networkPath := "/var/lib/docker/.../4d23e"
+//    networkNamespace, err := libnetwork.NewNamespace(networkPath)
+//    if err != nil {
+//    	return err
+//    }
+//
+//    // For each new container: allocate IP and interfaces. The returned network
+//    // settings will be used for container infos (inspect and such), as well as
+//    // iptables rules for port publishing.
+//    interfaces, err := network.CreateInterfaces(containerID)
+//    if err != nil {
+//    	return err
+//    }
+//
+//    // Add interfaces to the namespace.
+//    for _, interface := range interfaces {
+//    	if err := networkNamespace.AddInterface(interface); err != nil {
+//    		return err
+//    	}
+//    }
+package libnetwork
+
+import "fmt"
+
+type Network interface {
+	Name() string
+	Type() string
+	Link(name string) ([]*Interface, error)
+}
+
+type Interface struct {
+	// The name of the interface in the origin network namespace.
+	SrcName string
+
+	// The name that will be assigned to the interface once moves inside a
+	// network namespace.
+	DstName string
+
+	MacAddress  string
+	Address     string
+	AddressIPv6 string
+	Gateway     string
+	GatewayIPv6 string
+	MTU         int
+}
+
+type Namespace interface {
+	Path() string
+	Interfaces() []*Interface
+	AddInterface(*Interface) error
+}
+
+// TODO Figure out the proper options type
+func NewNetwork(networkType string, options strategyParams) (Network, error) {
+	if ctor, ok := strategies[networkType]; ok {
+		return ctor(options)
+	}
+	return nil, fmt.Errorf("Unknown network type %q", networkType)
+}

+ 45 - 0
libnetwork/reexec.go

@@ -0,0 +1,45 @@
+package libnetwork
+
+import (
+	"fmt"
+	"os"
+	"os/exec"
+
+	"github.com/docker/docker/pkg/reexec"
+)
+
+type ReexecCommand int
+
+const (
+	ReexecCreateNamespace ReexecCommand = iota
+	ReexecMoveInterface
+)
+
+var ReexecCommands = map[ReexecCommand]struct {
+	Key        string
+	Entrypoint func()
+}{
+	ReexecCreateNamespace: {"netns-create", createNetworkNamespace},
+	ReexecMoveInterface:   {"netns-moveif", namespaceMoveInterface},
+}
+
+func init() {
+	for _, reexecCmd := range ReexecCommands {
+		reexec.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:   reexec.Self(),
+		Args:   append([]string{reexecCommand.Key}, params...),
+		Stdout: os.Stdout,
+		Stderr: os.Stderr,
+	}
+	return cmd.Run()
+}

+ 78 - 0
libnetwork/reexec_move_interface.go

@@ -0,0 +1,78 @@
+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 namespaceMoveInterface() {
+	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)
+}

+ 52 - 0
libnetwork/reexec_netns_create.go

@@ -0,0 +1,52 @@
+package libnetwork
+
+import (
+	"log"
+	"os"
+	"runtime"
+	"syscall"
+
+	"github.com/vishvananda/netlink"
+)
+
+func createNetworkNamespace() {
+	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)
+}

+ 16 - 0
libnetwork/strategies.go

@@ -0,0 +1,16 @@
+package libnetwork
+
+import "fmt"
+
+type strategyParams map[string]interface{}
+type strategyConstructor func(strategyParams) (Network, error)
+
+var strategies = map[string]strategyConstructor{}
+
+func RegisterNetworkType(name string, ctor strategyConstructor) error {
+	if _, ok := strategies[name]; ok {
+		return fmt.Errorf("network type %q is already registed", name)
+	}
+	strategies[name] = ctor
+	return nil
+}

+ 34 - 0
libnetwork/system.go

@@ -0,0 +1,34 @@
+package libnetwork
+
+import (
+	"fmt"
+	"runtime"
+	"syscall"
+)
+
+// Via http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7b21fddd087678a70ad64afc0f632e0f1071b092
+//
+// We need different setns values for the different platforms and arch
+// We are declaring the macro here because the SETNS syscall does not exist in th stdlib
+var setNsMap = map[string]uintptr{
+	"linux/386":     346,
+	"linux/amd64":   308,
+	"linux/arm":     374,
+	"linux/ppc64":   350,
+	"linux/ppc64le": 350,
+	"linux/s390x":   339,
+}
+
+func Setns(fd uintptr, flags uintptr) error {
+	ns, exists := setNsMap[fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)]
+	if !exists {
+		return fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH)
+	}
+
+	_, _, err := syscall.RawSyscall(ns, fd, flags, 0)
+	if err != 0 {
+		return err
+	}
+
+	return nil
+}