Prechádzať zdrojové kódy

Refactor sandbox code to use interfaces

Currently sandbox code exposes bare structs
externally to the package. It is untenable
to continue this way and it becomes too
inflexible to use it to store internal state.
Changed all of them to use interfaces.
Also cleaned up a lot of boiler plate code
which needs to set into namespace.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
Jana Radhakrishnan 10 rokov pred
rodič
commit
f5c1c78179

+ 15 - 19
libnetwork/drivers/bridge/bridge.go

@@ -14,7 +14,6 @@ import (
 	"github.com/docker/libnetwork/netutils"
 	"github.com/docker/libnetwork/options"
 	"github.com/docker/libnetwork/portmapper"
-	"github.com/docker/libnetwork/sandbox"
 	"github.com/docker/libnetwork/types"
 	"github.com/vishvananda/netlink"
 )
@@ -70,7 +69,9 @@ type containerConfiguration struct {
 
 type bridgeEndpoint struct {
 	id              types.UUID
-	intf            *sandbox.Interface
+	srcName         string
+	addr            *net.IPNet
+	addrv6          *net.IPNet
 	macAddress      net.HardwareAddr
 	config          *endpointConfiguration // User specified parameters
 	containerConfig *containerConfiguration
@@ -802,25 +803,20 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
 	}
 
 	// Create the sandbox side pipe interface
-	intf := &sandbox.Interface{}
-	intf.SrcName = name2
-	intf.DstName = containerVethPrefix
-	intf.Address = ipv4Addr
+	endpoint.srcName = name2
+	endpoint.addr = ipv4Addr
 
 	if config.EnableIPv6 {
-		intf.AddressIPv6 = ipv6Addr
+		endpoint.addrv6 = ipv6Addr
 	}
 
-	// Store the interface in endpoint, this is needed for cleanup on DeleteEndpoint()
-	endpoint.intf = intf
-
 	err = epInfo.AddInterface(ifaceID, endpoint.macAddress, *ipv4Addr, *ipv6Addr)
 	if err != nil {
 		return err
 	}
 
 	// Program any required port mapping and store them in the endpoint
-	endpoint.portMapping, err = n.allocatePorts(epConfig, intf, config.DefaultBindingIP, config.EnableUserlandProxy)
+	endpoint.portMapping, err = n.allocatePorts(epConfig, endpoint, config.DefaultBindingIP, config.EnableUserlandProxy)
 	if err != nil {
 		return err
 	}
@@ -882,14 +878,14 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
 	n.releasePorts(ep)
 
 	// Release the v4 address allocated to this endpoint's sandbox interface
-	err = ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, ep.intf.Address.IP)
+	err = ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, ep.addr.IP)
 	if err != nil {
 		return err
 	}
 
 	// Release the v6 address allocated to this endpoint's sandbox interface
 	if config.EnableIPv6 {
-		err := ipAllocator.ReleaseIP(n.bridge.bridgeIPv6, ep.intf.AddressIPv6.IP)
+		err := ipAllocator.ReleaseIP(n.bridge.bridgeIPv6, ep.addrv6.IP)
 		if err != nil {
 			return err
 		}
@@ -897,7 +893,7 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
 
 	// Try removal of link. Discard error: link pair might have
 	// already been deleted by sandbox delete.
-	link, err := netlink.LinkByName(ep.intf.SrcName)
+	link, err := netlink.LinkByName(ep.srcName)
 	if err == nil {
 		netlink.LinkDel(link)
 	}
@@ -981,7 +977,7 @@ func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinI
 	for _, iNames := range jinfo.InterfaceNames() {
 		// Make sure to set names on the correct interface ID.
 		if iNames.ID() == ifaceID {
-			err = iNames.SetNames(endpoint.intf.SrcName, endpoint.intf.DstName)
+			err = iNames.SetNames(endpoint.srcName, containerVethPrefix)
 			if err != nil {
 				return err
 			}
@@ -1059,8 +1055,8 @@ func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options
 				return err
 			}
 
-			l := newLink(parentEndpoint.intf.Address.IP.String(),
-				endpoint.intf.Address.IP.String(),
+			l := newLink(parentEndpoint.addr.IP.String(),
+				endpoint.addr.IP.String(),
 				endpoint.config.ExposedPorts, network.config.BridgeName)
 			if enable {
 				err = l.Enable()
@@ -1092,8 +1088,8 @@ func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options
 			continue
 		}
 
-		l := newLink(endpoint.intf.Address.IP.String(),
-			childEndpoint.intf.Address.IP.String(),
+		l := newLink(endpoint.addr.IP.String(),
+			childEndpoint.addr.IP.String(),
 			childEndpoint.config.ExposedPorts, network.config.BridgeName)
 		if enable {
 			err = l.Enable()

+ 2 - 3
libnetwork/drivers/bridge/port_mapping.go

@@ -7,7 +7,6 @@ import (
 	"net"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/libnetwork/sandbox"
 	"github.com/docker/libnetwork/types"
 )
 
@@ -15,7 +14,7 @@ var (
 	defaultBindingIP = net.IPv4(0, 0, 0, 0)
 )
 
-func (n *bridgeNetwork) allocatePorts(epConfig *endpointConfiguration, intf *sandbox.Interface, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
+func (n *bridgeNetwork) allocatePorts(epConfig *endpointConfiguration, ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
 	if epConfig == nil || epConfig.PortBindings == nil {
 		return nil, nil
 	}
@@ -25,7 +24,7 @@ func (n *bridgeNetwork) allocatePorts(epConfig *endpointConfiguration, intf *san
 		defHostIP = reqDefBindIP
 	}
 
-	return n.allocatePortsInternal(epConfig.PortBindings, intf.Address.IP, defHostIP, ulPxyEnabled)
+	return n.allocatePortsInternal(epConfig.PortBindings, ep.addr.IP, defHostIP, ulPxyEnabled)
 }
 
 func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {

+ 0 - 176
libnetwork/sandbox/configure_linux.go

@@ -1,176 +0,0 @@
-package sandbox
-
-import (
-	"fmt"
-	"net"
-	"os"
-	"runtime"
-
-	"github.com/vishvananda/netlink"
-	"github.com/vishvananda/netns"
-)
-
-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)},
-		{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)},
-		{setInterfaceRoutes, fmt.Sprintf("error setting interface %q routes to %q", ifaceName, settings.Routes)},
-	}
-
-	for _, config := range ifaceConfigurators {
-		if err := config.Fn(iface, settings); err != nil {
-			return fmt.Errorf("%s: %v", config.ErrMessage, err)
-		}
-	}
-	return nil
-}
-
-func programGateway(path string, gw net.IP, isAdd bool) error {
-	runtime.LockOSThread()
-	defer runtime.UnlockOSThread()
-
-	origns, err := netns.Get()
-	if err != nil {
-		return err
-	}
-	defer origns.Close()
-
-	f, err := os.OpenFile(path, os.O_RDONLY, 0)
-	if err != nil {
-		return fmt.Errorf("failed get network namespace %q: %v", path, err)
-	}
-	defer f.Close()
-
-	nsFD := f.Fd()
-	if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
-		return err
-	}
-	defer netns.Set(origns)
-
-	gwRoutes, err := netlink.RouteGet(gw)
-	if err != nil {
-		return fmt.Errorf("route for the gateway could not be found: %v", err)
-	}
-
-	if isAdd {
-		return netlink.RouteAdd(&netlink.Route{
-			Scope:     netlink.SCOPE_UNIVERSE,
-			LinkIndex: gwRoutes[0].LinkIndex,
-			Gw:        gw,
-		})
-	}
-
-	return netlink.RouteDel(&netlink.Route{
-		Scope:     netlink.SCOPE_UNIVERSE,
-		LinkIndex: gwRoutes[0].LinkIndex,
-		Gw:        gw,
-	})
-}
-
-// Program a route in to the namespace routing table.
-func programRoute(path string, dest *net.IPNet, nh net.IP) error {
-	runtime.LockOSThread()
-	defer runtime.UnlockOSThread()
-
-	origns, err := netns.Get()
-	if err != nil {
-		return err
-	}
-	defer origns.Close()
-
-	f, err := os.OpenFile(path, os.O_RDONLY, 0)
-	if err != nil {
-		return fmt.Errorf("failed get network namespace %q: %v", path, err)
-	}
-	defer f.Close()
-
-	nsFD := f.Fd()
-	if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
-		return err
-	}
-	defer netns.Set(origns)
-
-	gwRoutes, err := netlink.RouteGet(nh)
-	if err != nil {
-		return fmt.Errorf("route for the next hop could not be found: %v", err)
-	}
-
-	return netlink.RouteAdd(&netlink.Route{
-		Scope:     netlink.SCOPE_UNIVERSE,
-		LinkIndex: gwRoutes[0].LinkIndex,
-		Gw:        gwRoutes[0].Gw,
-		Dst:       dest,
-	})
-}
-
-// Delete a route from the namespace routing table.
-func removeRoute(path string, dest *net.IPNet, nh net.IP) error {
-	runtime.LockOSThread()
-	defer runtime.UnlockOSThread()
-
-	origns, err := netns.Get()
-	if err != nil {
-		return err
-	}
-	defer origns.Close()
-
-	f, err := os.OpenFile(path, os.O_RDONLY, 0)
-	if err != nil {
-		return fmt.Errorf("failed get network namespace %q: %v", path, err)
-	}
-	defer f.Close()
-
-	nsFD := f.Fd()
-	if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
-		return err
-	}
-	defer netns.Set(origns)
-
-	gwRoutes, err := netlink.RouteGet(nh)
-	if err != nil {
-		return fmt.Errorf("route for the next hop could not be found: %v", err)
-	}
-
-	return netlink.RouteDel(&netlink.Route{
-		Scope:     netlink.SCOPE_UNIVERSE,
-		LinkIndex: gwRoutes[0].LinkIndex,
-		Gw:        gwRoutes[0].Gw,
-		Dst:       dest,
-	})
-}
-
-func setInterfaceIP(iface netlink.Link, settings *Interface) error {
-	ipAddr := &netlink.Addr{IPNet: settings.Address, Label: ""}
-	return netlink.AddrAdd(iface, ipAddr)
-}
-
-func setInterfaceIPv6(iface netlink.Link, settings *Interface) error {
-	if settings.AddressIPv6 == nil {
-		return nil
-	}
-	ipAddr := &netlink.Addr{IPNet: settings.AddressIPv6, Label: ""}
-	return netlink.AddrAdd(iface, ipAddr)
-}
-
-func setInterfaceName(iface netlink.Link, settings *Interface) error {
-	return netlink.LinkSetName(iface, settings.DstName)
-}
-
-func setInterfaceRoutes(iface netlink.Link, settings *Interface) error {
-	for _, route := range settings.Routes {
-		err := netlink.RouteAdd(&netlink.Route{
-			Scope:     netlink.SCOPE_LINK,
-			LinkIndex: iface.Attrs().Index,
-			Dst:       route,
-		})
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}

+ 284 - 0
libnetwork/sandbox/interface_linux.go

@@ -0,0 +1,284 @@
+package sandbox
+
+import (
+	"fmt"
+	"net"
+	"sync"
+
+	"github.com/docker/libnetwork/types"
+	"github.com/vishvananda/netlink"
+)
+
+// IfaceOption is a function option type to set interface options
+type IfaceOption func(i *nwIface)
+
+type nwIface struct {
+	srcName     string
+	dstName     string
+	address     *net.IPNet
+	addressIPv6 *net.IPNet
+	routes      []*net.IPNet
+	ns          *networkNamespace
+	sync.Mutex
+}
+
+func (i *nwIface) SrcName() string {
+	i.Lock()
+	defer i.Unlock()
+
+	return i.srcName
+}
+
+func (i *nwIface) DstName() string {
+	i.Lock()
+	defer i.Unlock()
+
+	return i.dstName
+}
+
+func (i *nwIface) DstMaster() string {
+	i.Lock()
+	defer i.Unlock()
+
+	return i.dstMaster
+}
+
+func (i *nwIface) Bridge() bool {
+	i.Lock()
+	defer i.Unlock()
+
+	return i.bridge
+}
+
+func (i *nwIface) Master() string {
+	i.Lock()
+	defer i.Unlock()
+
+	return i.master
+}
+
+func (i *nwIface) Address() *net.IPNet {
+	i.Lock()
+	defer i.Unlock()
+
+	return types.GetIPNetCopy(i.address)
+}
+
+func (i *nwIface) AddressIPv6() *net.IPNet {
+	i.Lock()
+	defer i.Unlock()
+
+	return types.GetIPNetCopy(i.addressIPv6)
+}
+
+func (i *nwIface) Routes() []*net.IPNet {
+	i.Lock()
+	defer i.Unlock()
+
+	routes := make([]*net.IPNet, len(i.routes))
+	for index, route := range i.routes {
+		r := types.GetIPNetCopy(route)
+		routes[index] = r
+	}
+
+	return routes
+}
+
+func (n *networkNamespace) Interfaces() []Interface {
+	n.Lock()
+	defer n.Unlock()
+
+	ifaces := make([]Interface, len(n.iFaces))
+
+	for i, iface := range n.iFaces {
+		ifaces[i] = iface
+	}
+
+	return ifaces
+}
+
+func (i *nwIface) Remove() error {
+	i.Lock()
+	n := i.ns
+	i.Unlock()
+
+	n.Lock()
+	path := n.path
+	n.Unlock()
+
+	return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
+		// Find the network inteerface identified by the DstName attribute.
+		iface, err := netlink.LinkByName(i.DstName())
+		if err != nil {
+			return err
+		}
+
+		// Down the interface before configuring
+		if err := netlink.LinkSetDown(iface); err != nil {
+			return err
+		}
+
+		err = netlink.LinkSetName(iface, i.SrcName())
+		if err != nil {
+			fmt.Println("LinkSetName failed: ", err)
+			return err
+		}
+
+		// if it is a bridge just delete it.
+		if i.Bridge() {
+			if err := netlink.LinkDel(iface); err != nil {
+				return fmt.Errorf("failed deleting bridge %q: %v", i.SrcName(), err)
+			}
+		} else {
+			// Move the network interface to caller namespace.
+			if err := netlink.LinkSetNsFd(iface, callerFD); err != nil {
+				fmt.Println("LinkSetNsPid failed: ", err)
+				return err
+			}
+		}
+
+		n.Lock()
+		for index, intf := range n.iFaces {
+			if intf == i {
+				n.iFaces = append(n.iFaces[:index], n.iFaces[index+1:]...)
+				break
+			}
+		}
+		n.Unlock()
+
+		return nil
+	})
+}
+
+func (n *networkNamespace) findDstMaster(srcName string) string {
+	n.Lock()
+	defer n.Unlock()
+
+	for _, i := range n.iFaces {
+		// The master should match the srcname of the interface and the
+		// master interface should be of type bridge.
+		if i.SrcName() == srcName && i.Bridge() {
+			return i.DstName()
+		}
+	}
+
+	return ""
+}
+
+func (n *networkNamespace) AddInterface(srcName, dstPrefix string, options ...IfaceOption) error {
+	i := &nwIface{srcName: srcName, dstName: dstPrefix, ns: n}
+	i.processInterfaceOptions(options...)
+
+	n.Lock()
+	i.dstName = fmt.Sprintf("%s%d", i.dstName, n.nextIfIndex)
+	n.nextIfIndex++
+	path := n.path
+	n.Unlock()
+
+	return nsInvoke(path, func(nsFD int) error {
+		// Find the network interface identified by the SrcName attribute.
+		iface, err := netlink.LinkByName(i.srcName)
+		if err != nil {
+			return fmt.Errorf("failed to get link by name %q: %v", i.srcName, err)
+		}
+
+		// Move the network interface to the destination namespace.
+		if err := netlink.LinkSetNsFd(iface, nsFD); err != nil {
+			return fmt.Errorf("failed to set namespace on link %q: %v", i.srcName, err)
+		}
+
+		return nil
+	}, func(callerFD int) error {
+		// Find the network interface identified by the SrcName attribute.
+		iface, err := netlink.LinkByName(i.srcName)
+		if err != nil {
+			return fmt.Errorf("failed to get link by name %q: %v", i.srcName, err)
+		}
+
+		// Down the interface before configuring
+		if err := netlink.LinkSetDown(iface); err != nil {
+			return fmt.Errorf("failed to set link down: %v", err)
+		}
+
+		// 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 fmt.Errorf("failed to set link up: %v", err)
+		}
+
+		n.Lock()
+		n.iFaces = append(n.iFaces, i)
+		n.Unlock()
+
+		return nil
+	})
+}
+
+func configureInterface(iface netlink.Link, i *nwIface) error {
+	ifaceName := iface.Attrs().Name
+	ifaceConfigurators := []struct {
+		Fn         func(netlink.Link, *nwIface) error
+		ErrMessage string
+	}{
+		{setInterfaceName, fmt.Sprintf("error renaming interface %q to %q", ifaceName, i.DstName())},
+		{setInterfaceIP, fmt.Sprintf("error setting interface %q IP to %q", ifaceName, i.Address())},
+		{setInterfaceIPv6, fmt.Sprintf("error setting interface %q IPv6 to %q", ifaceName, i.AddressIPv6())},
+		{setInterfaceRoutes, fmt.Sprintf("error setting interface %q routes to %q", ifaceName, i.Routes())},
+		{setInterfaceMaster, fmt.Sprintf("error setting interface %q master to %q", ifaceName, i.DstMaster())},
+	}
+
+	for _, config := range ifaceConfigurators {
+		if err := config.Fn(iface, i); err != nil {
+			return fmt.Errorf("%s: %v", config.ErrMessage, err)
+		}
+	}
+	return nil
+}
+
+func setInterfaceMaster(iface netlink.Link, i *nwIface) error {
+	if i.DstMaster() == "" {
+		return nil
+	}
+
+	return netlink.LinkSetMaster(iface, &netlink.Bridge{
+		LinkAttrs: netlink.LinkAttrs{Name: i.DstMaster()}})
+}
+
+func setInterfaceIP(iface netlink.Link, i *nwIface) error {
+	if i.Address() == nil {
+		return nil
+	}
+
+	ipAddr := &netlink.Addr{IPNet: i.Address(), Label: ""}
+	return netlink.AddrAdd(iface, ipAddr)
+}
+
+func setInterfaceIPv6(iface netlink.Link, i *nwIface) error {
+	if i.AddressIPv6() == nil {
+		return nil
+	}
+	ipAddr := &netlink.Addr{IPNet: i.AddressIPv6(), Label: ""}
+	return netlink.AddrAdd(iface, ipAddr)
+}
+
+func setInterfaceName(iface netlink.Link, i *nwIface) error {
+	return netlink.LinkSetName(iface, i.DstName())
+}
+
+func setInterfaceRoutes(iface netlink.Link, i *nwIface) error {
+	for _, route := range i.Routes() {
+		err := netlink.RouteAdd(&netlink.Route{
+			Scope:     netlink.SCOPE_LINK,
+			LinkIndex: iface.Attrs().Index,
+			Dst:       route,
+		})
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}

+ 31 - 206
libnetwork/sandbox/namespace_linux.go

@@ -32,9 +32,12 @@ var (
 // interface. It represents a linux network namespace, and moves an interface
 // into it when called on method AddInterface or sets the gateway etc.
 type networkNamespace struct {
-	path        string
-	sinfo       *Info
-	nextIfIndex int
+	path         string
+	iFaces       []*nwIface
+	gw           net.IP
+	gwv6         net.IP
+	staticRoutes []*types.StaticRoute
+	nextIfIndex  int
 	sync.Mutex
 }
 
@@ -127,12 +130,16 @@ func GenerateKey(containerID string) string {
 // NewSandbox provides a new sandbox instance created in an os specific way
 // provided a key which uniquely identifies the sandbox
 func NewSandbox(key string, osCreate bool) (Sandbox, error) {
-	info, err := createNetworkNamespace(key, osCreate)
+	err := createNetworkNamespace(key, osCreate)
 	if err != nil {
 		return nil, err
 	}
 
-	return &networkNamespace{path: key, sinfo: info}, nil
+	return &networkNamespace{path: key}, nil
+}
+
+func (n *networkNamespace) InterfaceOptions() IfaceOptionSetter {
+	return n
 }
 
 func reexecCreateNamespace() {
@@ -149,18 +156,18 @@ func reexecCreateNamespace() {
 	}
 }
 
-func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
+func createNetworkNamespace(path string, osCreate bool) error {
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
 	origns, err := netns.Get()
 	if err != nil {
-		return nil, err
+		return err
 	}
 	defer origns.Close()
 
 	if err := createNamespaceFile(path); err != nil {
-		return nil, err
+		return err
 	}
 
 	cmd := &exec.Cmd{
@@ -174,12 +181,10 @@ func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
 		cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNET
 	}
 	if err := cmd.Run(); err != nil {
-		return nil, fmt.Errorf("namespace creation reexec command failed: %v", err)
+		return fmt.Errorf("namespace creation reexec command failed: %v", err)
 	}
 
-	interfaces := []*Interface{}
-	info := &Info{Interfaces: interfaces}
-	return info, nil
+	return nil
 }
 
 func unmountNamespaceFile(path string) {
@@ -217,7 +222,7 @@ func loopbackUp() error {
 	return netlink.LinkSetUp(iface)
 }
 
-func (n *networkNamespace) RemoveInterface(i *Interface) error {
+func nsInvoke(path string, prefunc func(nsFD int) error, postfunc func(callerFD int) error) error {
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
@@ -227,84 +232,18 @@ func (n *networkNamespace) RemoveInterface(i *Interface) error {
 	}
 	defer origns.Close()
 
-	f, err := os.OpenFile(n.path, os.O_RDONLY, 0)
+	f, err := os.OpenFile(path, os.O_RDONLY, 0)
 	if err != nil {
-		return fmt.Errorf("failed get network namespace %q: %v", n.path, err)
+		return fmt.Errorf("failed get network namespace %q: %v", path, err)
 	}
 	defer f.Close()
 
 	nsFD := f.Fd()
-	if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
-		return err
-	}
-	defer netns.Set(origns)
 
-	// Find the network inteerface identified by the DstName attribute.
-	iface, err := netlink.LinkByName(i.DstName)
-	if err != nil {
-		return err
-	}
-
-	// Down the interface before configuring
-	if err := netlink.LinkSetDown(iface); err != nil {
-		return err
-	}
-
-	err = netlink.LinkSetName(iface, i.SrcName)
-	if err != nil {
-		fmt.Println("LinkSetName failed: ", err)
-		return err
-	}
-
-	// Move the network interface to caller namespace.
-	if err := netlink.LinkSetNsFd(iface, int(origns)); err != nil {
-		fmt.Println("LinkSetNsPid failed: ", err)
-		return err
-	}
-
-	n.Lock()
-	for index, intf := range n.sinfo.Interfaces {
-		if intf == i {
-			n.sinfo.Interfaces = append(n.sinfo.Interfaces[:index], n.sinfo.Interfaces[index+1:]...)
-			break
-		}
-	}
-	n.Unlock()
-
-	return nil
-}
-
-func (n *networkNamespace) AddInterface(i *Interface) error {
-	n.Lock()
-	i.DstName = fmt.Sprintf("%s%d", i.DstName, n.nextIfIndex)
-	n.nextIfIndex++
-	n.Unlock()
-
-	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 interface 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
+	// Invoked before the namespace switch happens but after the namespace file
+	// handle is obtained.
+	if err := prefunc(int(nsFD)); err != nil {
+		return fmt.Errorf("failed in prefunc: %v", err)
 	}
 
 	if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
@@ -312,133 +251,19 @@ func (n *networkNamespace) AddInterface(i *Interface) error {
 	}
 	defer netns.Set(origns)
 
-	// Down the interface before configuring
-	if err := netlink.LinkSetDown(iface); err != nil {
-		return err
-	}
-
-	// 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.Lock()
-	n.sinfo.Interfaces = append(n.sinfo.Interfaces, i)
-	n.Unlock()
-
-	return nil
+	// Invoked after the namespace switch.
+	return postfunc(int(origns))
 }
 
-func (n *networkNamespace) SetGateway(gw net.IP) error {
-	// Silently return if the gateway is empty
-	if len(gw) == 0 {
-		return nil
-	}
-
-	err := programGateway(n.path, gw, true)
-	if err == nil {
-		n.Lock()
-		n.sinfo.Gateway = gw
-		n.Unlock()
-	}
-
-	return err
-}
-
-func (n *networkNamespace) UnsetGateway() error {
+func (n *networkNamespace) nsPath() string {
 	n.Lock()
-	gw := n.sinfo.Gateway
-	n.Unlock()
-
-	// Silently return if the gateway is empty
-	if len(gw) == 0 {
-		return nil
-	}
-
-	err := programGateway(n.path, gw, false)
-	if err == nil {
-		n.Lock()
-		n.sinfo.Gateway = net.IP{}
-		n.Unlock()
-	}
-
-	return err
-}
-
-func (n *networkNamespace) SetGatewayIPv6(gw net.IP) error {
-	// Silently return if the gateway is empty
-	if len(gw) == 0 {
-		return nil
-	}
-
-	err := programGateway(n.path, gw, true)
-	if err == nil {
-		n.Lock()
-		n.sinfo.GatewayIPv6 = gw
-		n.Unlock()
-	}
-
-	return err
-}
-
-func (n *networkNamespace) UnsetGatewayIPv6() error {
-	n.Lock()
-	gw := n.sinfo.GatewayIPv6
-	n.Unlock()
-
-	// Silently return if the gateway is empty
-	if len(gw) == 0 {
-		return nil
-	}
-
-	err := programGateway(n.path, gw, false)
-	if err == nil {
-		n.Lock()
-		n.sinfo.GatewayIPv6 = net.IP{}
-		n.Unlock()
-	}
-
-	return err
-}
-
-func (n *networkNamespace) AddStaticRoute(r *types.StaticRoute) error {
-	err := programRoute(n.path, r.Destination, r.NextHop)
-	if err == nil {
-		n.Lock()
-		n.sinfo.StaticRoutes = append(n.sinfo.StaticRoutes, r)
-		n.Unlock()
-	}
-	return err
-}
+	defer n.Unlock()
 
-func (n *networkNamespace) RemoveStaticRoute(r *types.StaticRoute) error {
-	err := removeRoute(n.path, r.Destination, r.NextHop)
-	if err == nil {
-		n.Lock()
-		lastIndex := len(n.sinfo.StaticRoutes) - 1
-		for i, v := range n.sinfo.StaticRoutes {
-			if v == r {
-				// Overwrite the route we're removing with the last element
-				n.sinfo.StaticRoutes[i] = n.sinfo.StaticRoutes[lastIndex]
-				// Shorten the slice to trim the extra element
-				n.sinfo.StaticRoutes = n.sinfo.StaticRoutes[:lastIndex]
-				break
-			}
-		}
-		n.Unlock()
-	}
-	return err
+	return n.path
 }
 
-func (n *networkNamespace) Interfaces() []*Interface {
-	n.Lock()
-	defer n.Unlock()
-	return n.sinfo.Interfaces
+func (n *networkNamespace) Info() Info {
+	return n
 }
 
 func (n *networkNamespace) Key() string {

+ 29 - 0
libnetwork/sandbox/options_linux.go

@@ -0,0 +1,29 @@
+package sandbox
+
+import "net"
+
+func (i *nwIface) processInterfaceOptions(options ...IfaceOption) {
+	for _, opt := range options {
+		if opt != nil {
+			opt(i)
+		}
+	}
+}
+
+func (n *networkNamespace) Address(addr *net.IPNet) IfaceOption {
+	return func(i *nwIface) {
+		i.address = addr
+	}
+}
+
+func (n *networkNamespace) AddressIPv6(addr *net.IPNet) IfaceOption {
+	return func(i *nwIface) {
+		i.addressIPv6 = addr
+	}
+}
+
+func (n *networkNamespace) Routes(routes []*net.IPNet) IfaceOption {
+	return func(i *nwIface) {
+		i.routes = routes
+	}
+}

+ 198 - 0
libnetwork/sandbox/route_linux.go

@@ -0,0 +1,198 @@
+package sandbox
+
+import (
+	"fmt"
+	"net"
+
+	"github.com/docker/libnetwork/types"
+	"github.com/vishvananda/netlink"
+)
+
+func (n *networkNamespace) Gateway() net.IP {
+	n.Lock()
+	defer n.Unlock()
+
+	return n.gw
+}
+
+func (n *networkNamespace) GatewayIPv6() net.IP {
+	n.Lock()
+	defer n.Unlock()
+
+	return n.gwv6
+}
+
+func (n *networkNamespace) StaticRoutes() []*types.StaticRoute {
+	n.Lock()
+	defer n.Unlock()
+
+	routes := make([]*types.StaticRoute, len(n.staticRoutes))
+	for i, route := range n.staticRoutes {
+		r := route.GetCopy()
+		routes[i] = r
+	}
+
+	return routes
+}
+
+func (n *networkNamespace) setGateway(gw net.IP) {
+	n.Lock()
+	n.gw = gw
+	n.Unlock()
+}
+
+func (n *networkNamespace) setGatewayIPv6(gwv6 net.IP) {
+	n.Lock()
+	n.gwv6 = gwv6
+	n.Unlock()
+}
+
+func (n *networkNamespace) SetGateway(gw net.IP) error {
+	// Silently return if the gateway is empty
+	if len(gw) == 0 {
+		return nil
+	}
+
+	err := programGateway(n.nsPath(), gw, true)
+	if err == nil {
+		n.setGateway(gw)
+	}
+
+	return err
+}
+
+func (n *networkNamespace) UnsetGateway() error {
+	gw := n.Gateway()
+
+	// Silently return if the gateway is empty
+	if len(gw) == 0 {
+		return nil
+	}
+
+	err := programGateway(n.nsPath(), gw, false)
+	if err == nil {
+		n.setGateway(net.IP{})
+	}
+
+	return err
+}
+
+func programGateway(path string, gw net.IP, isAdd bool) error {
+	return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
+		gwRoutes, err := netlink.RouteGet(gw)
+		if err != nil {
+			return fmt.Errorf("route for the gateway could not be found: %v", err)
+		}
+
+		if isAdd {
+			return netlink.RouteAdd(&netlink.Route{
+				Scope:     netlink.SCOPE_UNIVERSE,
+				LinkIndex: gwRoutes[0].LinkIndex,
+				Gw:        gw,
+			})
+		}
+
+		return netlink.RouteDel(&netlink.Route{
+			Scope:     netlink.SCOPE_UNIVERSE,
+			LinkIndex: gwRoutes[0].LinkIndex,
+			Gw:        gw,
+		})
+	})
+}
+
+// Program a route in to the namespace routing table.
+func programRoute(path string, dest *net.IPNet, nh net.IP) error {
+	return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
+		gwRoutes, err := netlink.RouteGet(nh)
+		if err != nil {
+			return fmt.Errorf("route for the next hop could not be found: %v", err)
+		}
+
+		return netlink.RouteAdd(&netlink.Route{
+			Scope:     netlink.SCOPE_UNIVERSE,
+			LinkIndex: gwRoutes[0].LinkIndex,
+			Gw:        gwRoutes[0].Gw,
+			Dst:       dest,
+		})
+	})
+}
+
+// Delete a route from the namespace routing table.
+func removeRoute(path string, dest *net.IPNet, nh net.IP) error {
+	return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
+		gwRoutes, err := netlink.RouteGet(nh)
+		if err != nil {
+			return fmt.Errorf("route for the next hop could not be found: %v", err)
+		}
+
+		return netlink.RouteDel(&netlink.Route{
+			Scope:     netlink.SCOPE_UNIVERSE,
+			LinkIndex: gwRoutes[0].LinkIndex,
+			Gw:        gwRoutes[0].Gw,
+			Dst:       dest,
+		})
+	})
+}
+
+func (n *networkNamespace) SetGatewayIPv6(gwv6 net.IP) error {
+	// Silently return if the gateway is empty
+	if len(gwv6) == 0 {
+		return nil
+	}
+
+	err := programGateway(n.nsPath(), gwv6, true)
+	if err == nil {
+		n.SetGatewayIPv6(gwv6)
+	}
+
+	return err
+}
+
+func (n *networkNamespace) UnsetGatewayIPv6() error {
+	gwv6 := n.GatewayIPv6()
+
+	// Silently return if the gateway is empty
+	if len(gwv6) == 0 {
+		return nil
+	}
+
+	err := programGateway(n.nsPath(), gwv6, false)
+	if err == nil {
+		n.Lock()
+		n.gwv6 = net.IP{}
+		n.Unlock()
+	}
+
+	return err
+}
+
+func (n *networkNamespace) AddStaticRoute(r *types.StaticRoute) error {
+	err := programRoute(n.nsPath(), r.Destination, r.NextHop)
+	if err == nil {
+		n.Lock()
+		n.staticRoutes = append(n.staticRoutes, r)
+		n.Unlock()
+	}
+	return err
+}
+
+func (n *networkNamespace) RemoveStaticRoute(r *types.StaticRoute) error {
+	n.Lock()
+
+	err := removeRoute(n.nsPath(), r.Destination, r.NextHop)
+	if err == nil {
+		n.Lock()
+		lastIndex := len(n.staticRoutes) - 1
+		for i, v := range n.staticRoutes {
+			if v == r {
+				// Overwrite the route we're removing with the last element
+				n.staticRoutes[i] = n.staticRoutes[lastIndex]
+				// Shorten the slice to trim the extra element
+				n.staticRoutes = n.staticRoutes[:lastIndex]
+				break
+			}
+		}
+		n.Unlock()
+	}
+	return err
+}

+ 37 - 139
libnetwork/sandbox/sandbox.go

@@ -12,22 +12,12 @@ type Sandbox interface {
 	// The path where the network namespace is mounted.
 	Key() string
 
-	// The collection of Interface previously added with the AddInterface
-	// method. Note that this doesn't incude network interfaces added in any
-	// other way (such as the default loopback interface which are automatically
-	// created on creation of a sandbox).
-	Interfaces() []*Interface
-
 	// Add an existing Interface to this sandbox. The operation will rename
 	// from the Interface SrcName to DstName as it moves, and reconfigure the
 	// interface according to the specified settings. The caller is expected
 	// to only provide a prefix for DstName. The AddInterface api will auto-generate
 	// an appropriate suffix for the DstName to disambiguate.
-	AddInterface(*Interface) error
-
-	// Remove an interface from the sandbox by renaming to original name
-	// and moving it out of the sandbox.
-	RemoveInterface(*Interface) error
+	AddInterface(SrcName string, DstPrefix string, options ...IfaceOption) error
 
 	// Set default IPv4 gateway for the sandbox
 	SetGateway(gw net.IP) error
@@ -47,25 +37,47 @@ type Sandbox interface {
 	// Remove a static route from the sandbox.
 	RemoveStaticRoute(*types.StaticRoute) error
 
+	// Returns an interface with methods to set interface options.
+	InterfaceOptions() IfaceOptionSetter
+
+	// Returns an interface with methods to get sandbox state.
+	Info() Info
+
 	// Destroy the sandbox
 	Destroy() error
 }
 
+// IfaceOptionSetter interface defines the option setter methods for interface options.
+type IfaceOptionSetter interface {
+	// Address returns an option setter to set IPv4 address.
+	Address(*net.IPNet) IfaceOption
+
+	// Address returns an option setter to set IPv6 address.
+	AddressIPv6(*net.IPNet) IfaceOption
+
+	// Address returns an option setter to set interface routes.
+	Routes([]*net.IPNet) IfaceOption
+}
+
 // Info represents all possible information that
 // the driver wants to place in the sandbox which includes
 // interfaces, routes and gateway
-type Info struct {
-	Interfaces []*Interface
+type Info interface {
+	// The collection of Interface previously added with the AddInterface
+	// method. Note that this doesn't incude network interfaces added in any
+	// other way (such as the default loopback interface which are automatically
+	// created on creation of a sandbox).
+	Interfaces() []Interface
 
 	// IPv4 gateway for the sandbox.
-	Gateway net.IP
+	Gateway() net.IP
 
 	// IPv6 gateway for the sandbox.
-	GatewayIPv6 net.IP
+	GatewayIPv6() net.IP
 
 	// Additional static routes for the sandbox.  (Note that directly
 	// connected routes are stored on the particular interface they refer to.)
-	StaticRoutes []*types.StaticRoute
+	StaticRoutes() []*types.StaticRoute
 
 	// TODO: Add ip tables etc.
 }
@@ -75,140 +87,26 @@ type Info struct {
 // caller to use this information when moving interface SrcName from host
 // namespace to DstName in a different net namespace with the appropriate
 // network settings.
-type Interface struct {
+type Interface interface {
 	// The name of the interface in the origin network namespace.
-	SrcName string
+	SrcName() string
 
 	// The name that will be assigned to the interface once moves inside a
 	// network namespace. When the caller passes in a DstName, it is only
 	// expected to pass a prefix. The name will modified with an appropriately
 	// auto-generated suffix.
-	DstName string
+	DstName() string
 
 	// IPv4 address for the interface.
-	Address *net.IPNet
+	Address() *net.IPNet
 
 	// IPv6 address for the interface.
-	AddressIPv6 *net.IPNet
+	AddressIPv6() *net.IPNet
 
 	// IP routes for the interface.
-	Routes []*net.IPNet
-}
-
-// GetCopy returns a copy of this Interface structure
-func (i *Interface) GetCopy() *Interface {
-	copiedRoutes := make([]*net.IPNet, len(i.Routes))
-
-	for index := range i.Routes {
-		copiedRoutes[index] = types.GetIPNetCopy(i.Routes[index])
-	}
-
-	return &Interface{
-		SrcName:     i.SrcName,
-		DstName:     i.DstName,
-		Address:     types.GetIPNetCopy(i.Address),
-		AddressIPv6: types.GetIPNetCopy(i.AddressIPv6),
-		Routes:      copiedRoutes,
-	}
-}
-
-// Equal checks if this instance of Interface is equal to the passed one
-func (i *Interface) Equal(o *Interface) bool {
-	if i == o {
-		return true
-	}
-
-	if o == nil {
-		return false
-	}
-
-	if i.SrcName != o.SrcName || i.DstName != o.DstName {
-		return false
-	}
-
-	if !types.CompareIPNet(i.Address, o.Address) {
-		return false
-	}
-
-	if !types.CompareIPNet(i.AddressIPv6, o.AddressIPv6) {
-		return false
-	}
-
-	if len(i.Routes) != len(o.Routes) {
-		return false
-	}
-
-	for index := range i.Routes {
-		if !types.CompareIPNet(i.Routes[index], o.Routes[index]) {
-			return false
-		}
-	}
-
-	return true
-}
-
-// GetCopy returns a copy of this SandboxInfo structure
-func (s *Info) GetCopy() *Info {
-	list := make([]*Interface, len(s.Interfaces))
-	for i, iface := range s.Interfaces {
-		list[i] = iface.GetCopy()
-	}
-	gw := types.GetIPCopy(s.Gateway)
-	gw6 := types.GetIPCopy(s.GatewayIPv6)
-
-	routes := make([]*types.StaticRoute, len(s.StaticRoutes))
-	for i, r := range s.StaticRoutes {
-		routes[i] = r.GetCopy()
-	}
-
-	return &Info{Interfaces: list,
-		Gateway:      gw,
-		GatewayIPv6:  gw6,
-		StaticRoutes: routes}
-}
-
-// Equal checks if this instance of SandboxInfo is equal to the passed one
-func (s *Info) Equal(o *Info) bool {
-	if s == o {
-		return true
-	}
-
-	if o == nil {
-		return false
-	}
-
-	if !s.Gateway.Equal(o.Gateway) {
-		return false
-	}
-
-	if !s.GatewayIPv6.Equal(o.GatewayIPv6) {
-		return false
-	}
-
-	if (s.Interfaces == nil && o.Interfaces != nil) ||
-		(s.Interfaces != nil && o.Interfaces == nil) ||
-		(len(s.Interfaces) != len(o.Interfaces)) {
-		return false
-	}
-
-	// Note: At the moment, the two lists must be in the same order
-	for i := 0; i < len(s.Interfaces); i++ {
-		if !s.Interfaces[i].Equal(o.Interfaces[i]) {
-			return false
-		}
-	}
-
-	for index := range s.StaticRoutes {
-		ss := s.StaticRoutes[index]
-		oo := o.StaticRoutes[index]
-		if !types.CompareIPNet(ss.Destination, oo.Destination) {
-			return false
-		}
-		if !ss.NextHop.Equal(oo.NextHop) {
-			return false
-		}
-	}
-
-	return true
+	Routes() []*net.IPNet
 
+	// Remove an interface from the sandbox by renaming to original name
+	// and moving it out of the sandbox.
+	Remove() error
 }

+ 27 - 31
libnetwork/sandbox/sandbox_linux_test.go

@@ -40,7 +40,7 @@ func newKey(t *testing.T) (string, error) {
 	return name, nil
 }
 
-func newInfo(t *testing.T) (*Info, error) {
+func newInfo(t *testing.T) (Sandbox, error) {
 	veth := &netlink.Veth{
 		LinkAttrs: netlink.LinkAttrs{Name: vethName1, TxQLen: 0},
 		PeerName:  vethName2}
@@ -50,31 +50,31 @@ func newInfo(t *testing.T) (*Info, error) {
 
 	// Store the sandbox side pipe interface
 	// This is needed for cleanup on DeleteEndpoint()
-	intf1 := &Interface{}
-	intf1.SrcName = vethName2
-	intf1.DstName = sboxIfaceName
+	intf1 := &nwIface{}
+	intf1.srcName = vethName2
+	intf1.dstName = sboxIfaceName
 
 	ip4, addr, err := net.ParseCIDR("192.168.1.100/24")
 	if err != nil {
 		return nil, err
 	}
-	intf1.Address = addr
-	intf1.Address.IP = ip4
+	intf1.address = addr
+	intf1.address.IP = ip4
 
 	// ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48")
 	ip6, addrv6, err := net.ParseCIDR("fe80::2/64")
 	if err != nil {
 		return nil, err
 	}
-	intf1.AddressIPv6 = addrv6
-	intf1.AddressIPv6.IP = ip6
+	intf1.addressIPv6 = addrv6
+	intf1.addressIPv6.IP = ip6
 
 	_, route, err := net.ParseCIDR("192.168.2.1/32")
 	if err != nil {
 		return nil, err
 	}
 
-	intf1.Routes = []*net.IPNet{route}
+	intf1.routes = []*net.IPNet{route}
 
 	veth = &netlink.Veth{
 		LinkAttrs: netlink.LinkAttrs{Name: vethName3, TxQLen: 0},
@@ -84,16 +84,16 @@ func newInfo(t *testing.T) (*Info, error) {
 		return nil, err
 	}
 
-	intf2 := &Interface{}
-	intf2.SrcName = vethName4
-	intf2.DstName = sboxIfaceName
+	intf2 := &nwIface{}
+	intf2.srcName = vethName4
+	intf2.dstName = sboxIfaceName
 
 	ip4, addr, err = net.ParseCIDR("192.168.2.100/24")
 	if err != nil {
 		return nil, err
 	}
-	intf2.Address = addr
-	intf2.Address.IP = ip4
+	intf2.address = addr
+	intf2.address.IP = ip4
 
 	// ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48")
 	ip6, addrv6, err = net.ParseCIDR("fe80::3/64")
@@ -101,19 +101,19 @@ func newInfo(t *testing.T) (*Info, error) {
 	if err != nil {
 		return nil, err
 	}
-	intf2.AddressIPv6 = addrv6
-	intf2.AddressIPv6.IP = ip6
+	intf2.addressIPv6 = addrv6
+	intf2.addressIPv6.IP = ip6
 
-	sinfo := &Info{Interfaces: []*Interface{intf1, intf2}}
+	info := &networkNamespace{iFaces: []*nwIface{intf1, intf2}}
 
-	sinfo.Gateway = net.ParseIP("192.168.1.1")
+	info.gw = net.ParseIP("192.168.1.1")
 	// sinfo.GatewayIPv6 = net.ParseIP("2001:DB8::1")
-	sinfo.GatewayIPv6 = net.ParseIP("fe80::1")
+	info.gwv6 = net.ParseIP("fe80::1")
 
-	return sinfo, nil
+	return info, nil
 }
 
-func verifySandbox(t *testing.T, s Sandbox) {
+func verifySandbox(t *testing.T, s Sandbox, ifaceSuffixes []string) {
 	_, ok := s.(*networkNamespace)
 	if !ok {
 		t.Fatalf("The sandox interface returned is not of type networkNamespace")
@@ -140,16 +140,12 @@ func verifySandbox(t *testing.T, s Sandbox) {
 	}
 	defer netns.Set(origns)
 
-	_, err = netlink.LinkByName(sboxIfaceName + "0")
-	if err != nil {
-		t.Fatalf("Could not find the interface %s inside the sandbox: %v", sboxIfaceName+"0",
-			err)
-	}
-
-	_, err = netlink.LinkByName(sboxIfaceName + "1")
-	if err != nil {
-		t.Fatalf("Could not find the interface %s inside the sandbox: %v", sboxIfaceName+"1",
-			err)
+	for _, suffix := range ifaceSuffixes {
+		_, err = netlink.LinkByName(sboxIfaceName + suffix)
+		if err != nil {
+			t.Fatalf("Could not find the interface %s inside the sandbox: %v",
+				sboxIfaceName+suffix, err)
+		}
 	}
 }
 

+ 45 - 107
libnetwork/sandbox/sandbox_test.go

@@ -1,11 +1,12 @@
 package sandbox
 
 import (
-	"net"
 	"os"
+	"runtime"
 	"testing"
 
 	"github.com/docker/docker/pkg/reexec"
+	"github.com/docker/libnetwork/netutils"
 )
 
 func TestMain(m *testing.M) {
@@ -16,6 +17,8 @@ func TestMain(m *testing.M) {
 }
 
 func TestSandboxCreate(t *testing.T) {
+	defer netutils.SetupTestNetNS(t)()
+
 	key, err := newKey(t)
 	if err != nil {
 		t.Fatalf("Failed to obtain a key: %v", err)
@@ -25,39 +28,49 @@ func TestSandboxCreate(t *testing.T) {
 	if err != nil {
 		t.Fatalf("Failed to create a new sandbox: %v", err)
 	}
+	runtime.LockOSThread()
 
 	if s.Key() != key {
 		t.Fatalf("s.Key() returned %s. Expected %s", s.Key(), key)
 	}
 
-	info, err := newInfo(t)
+	tbox, err := newInfo(t)
 	if err != nil {
 		t.Fatalf("Failed to generate new sandbox info: %v", err)
 	}
 
-	for _, i := range info.Interfaces {
-		err = s.AddInterface(i)
+	for _, i := range tbox.Info().Interfaces() {
+		err = s.AddInterface(i.SrcName(), i.DstName(),
+			tbox.InterfaceOptions().Address(i.Address()),
+			tbox.InterfaceOptions().AddressIPv6(i.AddressIPv6()))
 		if err != nil {
 			t.Fatalf("Failed to add interfaces to sandbox: %v", err)
 		}
+		runtime.LockOSThread()
 	}
 
-	err = s.SetGateway(info.Gateway)
+	err = s.SetGateway(tbox.Info().Gateway())
 	if err != nil {
 		t.Fatalf("Failed to set gateway to sandbox: %v", err)
 	}
+	runtime.LockOSThread()
 
-	err = s.SetGatewayIPv6(info.GatewayIPv6)
+	err = s.SetGatewayIPv6(tbox.Info().GatewayIPv6())
 	if err != nil {
 		t.Fatalf("Failed to set ipv6 gateway to sandbox: %v", err)
 	}
+	runtime.LockOSThread()
+
+	verifySandbox(t, s, []string{"0", "1"})
+	runtime.LockOSThread()
 
-	verifySandbox(t, s)
 	s.Destroy()
 	verifyCleanup(t, s, true)
 }
 
 func TestSandboxCreateTwice(t *testing.T) {
+	defer netutils.SetupTestNetNS(t)()
+
 	key, err := newKey(t)
 	if err != nil {
 		t.Fatalf("Failed to obtain a key: %v", err)
@@ -67,6 +80,7 @@ func TestSandboxCreateTwice(t *testing.T) {
 	if err != nil {
 		t.Fatalf("Failed to create a new sandbox: %v", err)
 	}
+	runtime.LockOSThread()
 
 	// Create another sandbox with the same key to see if we handle it
 	// gracefully.
@@ -74,6 +88,7 @@ func TestSandboxCreateTwice(t *testing.T) {
 	if err != nil {
 		t.Fatalf("Failed to create a new sandbox: %v", err)
 	}
+	runtime.LockOSThread()
 	s.Destroy()
 }
 
@@ -95,6 +110,8 @@ func TestSandboxGC(t *testing.T) {
 }
 
 func TestAddRemoveInterface(t *testing.T) {
+	defer netutils.SetupTestNetNS(t)()
+
 	key, err := newKey(t)
 	if err != nil {
 		t.Fatalf("Failed to obtain a key: %v", err)
@@ -104,128 +121,49 @@ func TestAddRemoveInterface(t *testing.T) {
 	if err != nil {
 		t.Fatalf("Failed to create a new sandbox: %v", err)
 	}
+	runtime.LockOSThread()
 
 	if s.Key() != key {
 		t.Fatalf("s.Key() returned %s. Expected %s", s.Key(), key)
 	}
 
-	info, err := newInfo(t)
+	tbox, err := newInfo(t)
 	if err != nil {
 		t.Fatalf("Failed to generate new sandbox info: %v", err)
 	}
 
-	for _, i := range info.Interfaces {
-		err = s.AddInterface(i)
+	for _, i := range tbox.Info().Interfaces() {
+		err = s.AddInterface(i.SrcName(), i.DstName(),
+			tbox.InterfaceOptions().Address(i.Address()),
+			tbox.InterfaceOptions().AddressIPv6(i.AddressIPv6()))
 		if err != nil {
 			t.Fatalf("Failed to add interfaces to sandbox: %v", err)
 		}
+		runtime.LockOSThread()
 	}
 
-	interfaces := s.Interfaces()
-	if !(interfaces[0].Equal(info.Interfaces[0]) && interfaces[1].Equal(info.Interfaces[1])) {
-		t.Fatalf("Failed to update Sandbox.sinfo.Interfaces in AddInterfaces")
-	}
+	verifySandbox(t, s, []string{"0", "1"})
+	runtime.LockOSThread()
 
-	if err := s.RemoveInterface(info.Interfaces[0]); err != nil {
+	interfaces := s.Info().Interfaces()
+	if err := interfaces[0].Remove(); err != nil {
 		t.Fatalf("Failed to remove interfaces from sandbox: %v", err)
 	}
+	runtime.LockOSThread()
 
-	if !s.Interfaces()[0].Equal(info.Interfaces[1]) {
-		t.Fatalf("Failed to update the sanbox.sinfo.Interfaces in RemoveInterferce")
-	}
+	verifySandbox(t, s, []string{"1"})
+	runtime.LockOSThread()
 
-	if err := s.AddInterface(info.Interfaces[0]); err != nil {
+	i := tbox.Info().Interfaces()[0]
+	if err := s.AddInterface(i.SrcName(), i.DstName(),
+		tbox.InterfaceOptions().Address(i.Address()),
+		tbox.InterfaceOptions().AddressIPv6(i.AddressIPv6())); err != nil {
 		t.Fatalf("Failed to add interfaces to sandbox: %v", err)
 	}
+	runtime.LockOSThread()
 
-	interfaces = s.Interfaces()
-	if !(interfaces[0].Equal(info.Interfaces[1]) && interfaces[1].Equal(info.Interfaces[0])) {
-		t.Fatalf("Failed to update Sandbox.sinfo.Interfaces in AddInterfaces")
-	}
+	verifySandbox(t, s, []string{"1", "2"})
+	runtime.LockOSThread()
 
 	s.Destroy()
 }
-
-func TestInterfaceEqual(t *testing.T) {
-	list := getInterfaceList()
-
-	if !list[0].Equal(list[0]) {
-		t.Fatalf("Interface.Equal() returned false negative")
-	}
-
-	if list[0].Equal(list[1]) {
-		t.Fatalf("Interface.Equal() returned false positive")
-	}
-
-	if list[0].Equal(list[1]) != list[1].Equal(list[0]) {
-		t.Fatalf("Interface.Equal() failed commutative check")
-	}
-}
-
-func TestSandboxInfoEqual(t *testing.T) {
-	si1 := &Info{Interfaces: getInterfaceList(), Gateway: net.ParseIP("192.168.1.254"), GatewayIPv6: net.ParseIP("2001:2345::abcd:8889")}
-	si2 := &Info{Interfaces: getInterfaceList(), Gateway: net.ParseIP("172.18.255.254"), GatewayIPv6: net.ParseIP("2001:2345::abcd:8888")}
-
-	if !si1.Equal(si1) {
-		t.Fatalf("Info.Equal() returned false negative")
-	}
-
-	if si1.Equal(si2) {
-		t.Fatalf("Info.Equal() returned false positive")
-	}
-
-	if si1.Equal(si2) != si2.Equal(si1) {
-		t.Fatalf("Info.Equal() failed commutative check")
-	}
-}
-
-func TestInterfaceCopy(t *testing.T) {
-	for _, iface := range getInterfaceList() {
-		cp := iface.GetCopy()
-
-		if !iface.Equal(cp) {
-			t.Fatalf("Failed to return a copy of Interface")
-		}
-
-		if iface == cp {
-			t.Fatalf("Failed to return a true copy of Interface")
-		}
-	}
-}
-
-func TestSandboxInfoCopy(t *testing.T) {
-	si := Info{Interfaces: getInterfaceList(), Gateway: net.ParseIP("192.168.1.254"), GatewayIPv6: net.ParseIP("2001:2345::abcd:8889")}
-	cp := si.GetCopy()
-
-	if !si.Equal(cp) {
-		t.Fatalf("Failed to return a copy of Info")
-	}
-
-	if &si == cp {
-		t.Fatalf("Failed to return a true copy of Info")
-	}
-}
-
-func getInterfaceList() []*Interface {
-	_, netv4a, _ := net.ParseCIDR("192.168.30.1/24")
-	_, netv4b, _ := net.ParseCIDR("172.18.255.2/23")
-	_, netv6a, _ := net.ParseCIDR("2001:2345::abcd:8888/80")
-	_, netv6b, _ := net.ParseCIDR("2001:2345::abcd:8889/80")
-
-	return []*Interface{
-		&Interface{
-			SrcName:     "veth1234567",
-			DstName:     "eth0",
-			Address:     netv4a,
-			AddressIPv6: netv6a,
-			Routes:      []*net.IPNet{netv4a, netv6a},
-		},
-		&Interface{
-			SrcName:     "veth7654321",
-			DstName:     "eth1",
-			Address:     netv4b,
-			AddressIPv6: netv6b,
-			Routes:      []*net.IPNet{netv4b, netv6b},
-		},
-	}
-}

+ 10 - 11
libnetwork/sandboxdata.go

@@ -83,17 +83,16 @@ func (s *sandboxData) addEndpoint(ep *endpoint) error {
 
 	sb := s.sandbox()
 	for _, i := range ifaces {
-		iface := &sandbox.Interface{
-			SrcName: i.srcName,
-			DstName: i.dstPrefix,
-			Address: &i.addr,
-			Routes:  i.routes,
-		}
+		var ifaceOptions []sandbox.IfaceOption
+
+		ifaceOptions = append(ifaceOptions, sb.InterfaceOptions().Address(&i.addr),
+			sb.InterfaceOptions().Routes(i.routes))
 		if i.addrv6.IP.To16() != nil {
-			iface.AddressIPv6 = &i.addrv6
+			ifaceOptions = append(ifaceOptions,
+				sb.InterfaceOptions().AddressIPv6(&i.addrv6))
 		}
 
-		if err := sb.AddInterface(iface); err != nil {
+		if err := sb.AddInterface(i.srcName, i.dstPrefix, ifaceOptions...); err != nil {
 			return err
 		}
 	}
@@ -131,10 +130,10 @@ func (s *sandboxData) rmEndpoint(ep *endpoint) int {
 	ep.Unlock()
 
 	sb := s.sandbox()
-	for _, i := range sb.Interfaces() {
+	for _, i := range sb.Info().Interfaces() {
 		// Only remove the interfaces owned by this endpoint from the sandbox.
-		if ep.hasInterface(i.SrcName) {
-			if err := sb.RemoveInterface(i); err != nil {
+		if ep.hasInterface(i.SrcName()) {
+			if err := i.Remove(); err != nil {
 				logrus.Debugf("Remove interface failed: %v", err)
 			}
 		}