Pārlūkot izejas kodu

Merge pull request #40 from mrjana/cnm

Libnetwork initial refactor for container network model
Madhu Venugopal 10 gadi atpakaļ
vecāks
revīzija
dc7e065b01
36 mainītis faili ar 1111 papildinājumiem un 482 dzēšanām
  1. 14 1
      libnetwork/Godeps/Godeps.json
  2. 47 0
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/common/randomid.go
  3. 59 0
      libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/common/randomid_test.go
  4. 68 0
      libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/utils/utils.go
  5. 15 0
      libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/utils/utils_test.go
  6. 37 28
      libnetwork/README.md
  7. 4 3
      libnetwork/cmd/test/main.go
  8. 0 81
      libnetwork/configure.go
  9. 72 0
      libnetwork/driverapi/driverapi.go
  10. 9 56
      libnetwork/drivers.go
  11. 286 10
      libnetwork/drivers/bridge/bridge.go
  12. 10 18
      libnetwork/drivers/bridge/bridge_test.go
  13. 3 3
      libnetwork/drivers/bridge/interface_test.go
  14. 0 128
      libnetwork/drivers/bridge/network.go
  15. 31 27
      libnetwork/drivers/bridge/network_test.go
  16. 2 2
      libnetwork/drivers/bridge/setup_device.go
  17. 7 7
      libnetwork/drivers/bridge/setup_device_test.go
  18. 3 3
      libnetwork/drivers/bridge/setup_fixedcidrv4_test.go
  19. 2 2
      libnetwork/drivers/bridge/setup_fixedcidrv6_test.go
  20. 2 2
      libnetwork/drivers/bridge/setup_ip_tables.go
  21. 3 3
      libnetwork/drivers/bridge/setup_ip_tables_test.go
  22. 3 3
      libnetwork/drivers/bridge/setup_ipv4.go
  23. 3 3
      libnetwork/drivers/bridge/setup_ipv4_test.go
  24. 2 2
      libnetwork/drivers/bridge/setup_ipv6_test.go
  25. 6 6
      libnetwork/drivers/bridge/setup_verify_test.go
  26. 3 3
      libnetwork/ipallocator/allocator.go
  27. 5 2
      libnetwork/libnetwork_test.go
  28. 1 1
      libnetwork/netutils/test_utils.go
  29. 1 1
      libnetwork/netutils/utils.go
  30. 1 1
      libnetwork/netutils/utils_test.go
  31. 209 74
      libnetwork/network.go
  32. 66 0
      libnetwork/sandbox/configure_linux.go
  33. 37 12
      libnetwork/sandbox/namespace_linux.go
  34. 25 0
      libnetwork/sandbox/sandbox.go
  35. 54 0
      libnetwork/sandbox/sandbox_linux_test.go
  36. 21 0
      libnetwork/sandbox/sandbox_test.go

+ 14 - 1
libnetwork/Godeps/Godeps.json

@@ -1,12 +1,20 @@
 {
 	"ImportPath": "github.com/docker/libnetwork",
-	"GoVersion": "go1.2.1",
+	"GoVersion": "go1.4.2",
+	"Packages": [
+		"./..."
+	],
 	"Deps": [
 		{
 			"ImportPath": "github.com/Sirupsen/logrus",
 			"Comment": "v0.6.4-12-g467d9d5",
 			"Rev": "467d9d55c2d2c17248441a8fc661561161f40d5e"
 		},
+		{
+			"ImportPath": "github.com/docker/docker/pkg/common",
+			"Comment": "v1.4.1-1379-g8e107a9",
+			"Rev": "8e107a93210c54f22ec1354d969c771b1abfbe05"
+		},
 		{
 			"ImportPath": "github.com/docker/docker/pkg/iptables",
 			"Comment": "v1.4.1-2492-ge690ad9",
@@ -27,6 +35,11 @@
 			"Comment": "v1.4.1-2492-ge690ad9",
 			"Rev": "e690ad92925a045344bde8d2d59d7a7f602dded6"
 		},
+		{
+			"ImportPath": "github.com/docker/libcontainer/utils",
+			"Comment": "v1.4.0-324-g88989e6",
+			"Rev": "88989e66d3a1ab960deb37f3dd7f824d85e1b9bc"
+		},
 		{
 			"ImportPath": "github.com/vishvananda/netlink",
 			"Rev": "8eb64238879fed52fd51c5b30ad20b928fb4c36c"

+ 47 - 0
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/common/randomid.go

@@ -0,0 +1,47 @@
+package common
+
+import (
+	"crypto/rand"
+	"encoding/hex"
+	"io"
+	"strconv"
+)
+
+// TruncateID returns a shorthand version of a string identifier for convenience.
+// A collision with other shorthands is very unlikely, but possible.
+// In case of a collision a lookup with TruncIndex.Get() will fail, and the caller
+// will need to use a langer prefix, or the full-length Id.
+func TruncateID(id string) string {
+	shortLen := 12
+	if len(id) < shortLen {
+		shortLen = len(id)
+	}
+	return id[:shortLen]
+}
+
+// GenerateRandomID returns an unique id
+func GenerateRandomID() string {
+	for {
+		id := make([]byte, 32)
+		if _, err := io.ReadFull(rand.Reader, id); err != nil {
+			panic(err) // This shouldn't happen
+		}
+		value := hex.EncodeToString(id)
+		// if we try to parse the truncated for as an int and we don't have
+		// an error then the value is all numberic and causes issues when
+		// used as a hostname. ref #3869
+		if _, err := strconv.ParseInt(TruncateID(value), 10, 64); err == nil {
+			continue
+		}
+		return value
+	}
+}
+
+func RandomString() string {
+	id := make([]byte, 32)
+
+	if _, err := io.ReadFull(rand.Reader, id); err != nil {
+		panic(err) // This shouldn't happen
+	}
+	return hex.EncodeToString(id)
+}

+ 59 - 0
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/common/randomid_test.go

@@ -0,0 +1,59 @@
+package common
+
+import (
+	"testing"
+)
+
+func TestShortenId(t *testing.T) {
+	id := GenerateRandomID()
+	truncID := TruncateID(id)
+	if len(truncID) != 12 {
+		t.Fatalf("Id returned is incorrect: truncate on %s returned %s", id, truncID)
+	}
+}
+
+func TestShortenIdEmpty(t *testing.T) {
+	id := ""
+	truncID := TruncateID(id)
+	if len(truncID) > len(id) {
+		t.Fatalf("Id returned is incorrect: truncate on %s returned %s", id, truncID)
+	}
+}
+
+func TestShortenIdInvalid(t *testing.T) {
+	id := "1234"
+	truncID := TruncateID(id)
+	if len(truncID) != len(id) {
+		t.Fatalf("Id returned is incorrect: truncate on %s returned %s", id, truncID)
+	}
+}
+
+func TestGenerateRandomID(t *testing.T) {
+	id := GenerateRandomID()
+
+	if len(id) != 64 {
+		t.Fatalf("Id returned is incorrect: %s", id)
+	}
+}
+
+func TestRandomString(t *testing.T) {
+	id := RandomString()
+	if len(id) != 64 {
+		t.Fatalf("Id returned is incorrect: %s", id)
+	}
+}
+
+func TestRandomStringUniqueness(t *testing.T) {
+	repeats := 25
+	set := make(map[string]struct{}, repeats)
+	for i := 0; i < repeats; i = i + 1 {
+		id := RandomString()
+		if len(id) != 64 {
+			t.Fatalf("Id returned is incorrect: %s", id)
+		}
+		if _, ok := set[id]; ok {
+			t.Fatalf("Random number is repeated")
+		}
+		set[id] = struct{}{}
+	}
+}

+ 68 - 0
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/utils/utils.go

@@ -0,0 +1,68 @@
+package utils
+
+import (
+	"crypto/rand"
+	"encoding/hex"
+	"io"
+	"io/ioutil"
+	"path/filepath"
+	"strconv"
+	"syscall"
+)
+
+const (
+	exitSignalOffset = 128
+)
+
+// GenerateRandomName returns a new name joined with a prefix.  This size
+// specified is used to truncate the randomly generated value
+func GenerateRandomName(prefix string, size int) (string, error) {
+	id := make([]byte, 32)
+	if _, err := io.ReadFull(rand.Reader, id); err != nil {
+		return "", err
+	}
+	return prefix + hex.EncodeToString(id)[:size], nil
+}
+
+// ResolveRootfs ensures that the current working directory is
+// not a symlink and returns the absolute path to the rootfs
+func ResolveRootfs(uncleanRootfs string) (string, error) {
+	rootfs, err := filepath.Abs(uncleanRootfs)
+	if err != nil {
+		return "", err
+	}
+	return filepath.EvalSymlinks(rootfs)
+}
+
+func CloseExecFrom(minFd int) error {
+	fdList, err := ioutil.ReadDir("/proc/self/fd")
+	if err != nil {
+		return err
+	}
+	for _, fi := range fdList {
+		fd, err := strconv.Atoi(fi.Name())
+		if err != nil {
+			// ignore non-numeric file names
+			continue
+		}
+
+		if fd < minFd {
+			// ignore descriptors lower than our specified minimum
+			continue
+		}
+
+		// intentionally ignore errors from syscall.CloseOnExec
+		syscall.CloseOnExec(fd)
+		// the cases where this might fail are basically file descriptors that have already been closed (including and especially the one that was created when ioutil.ReadDir did the "opendir" syscall)
+	}
+	return nil
+}
+
+// ExitStatus returns the correct exit status for a process based on if it
+// was signaled or existed cleanly.
+func ExitStatus(status syscall.WaitStatus) int {
+	if status.Signaled() {
+		return exitSignalOffset + int(status.Signal())
+	}
+	return status.ExitStatus()
+}

+ 15 - 0
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/utils/utils_test.go

@@ -0,0 +1,15 @@
+package utils
+
+import "testing"
+
+func TestGenerateName(t *testing.T) {
+	name, err := GenerateRandomName("veth", 5)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	expected := 5 + len("veth")
+	if len(name) != 5+len("veth") {
+		t.Fatalf("expected name to be %d chars but received %d", expected, len(name))
+	}
+}

+ 37 - 28
libnetwork/README.md

@@ -17,34 +17,43 @@ Please refer to the [roadmap](ROADMAP.md) for more information.
 There are many networking solutions available to suit a broad range of use-cases. libnetwork uses a driver / plugin model to support all of these solutions while abstracting the complexity of the driver implementations by exposing a simple and consistent Network Model to users.
 
 ```go
-//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.NewNetworkNamespace(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.Link(containerID)
-if err != nil {
-  return err
-}
-//
-// Add interfaces to the namespace.
-for _, interface := range interfaces {
-  if err := networkNamespace.AddInterface(interface); err != nil {
-          return err
-  }
-}
-//
+ // Create a new controller instance
+ controller := libnetwork.New()
+ 
+ options := options.Generic{}
+ // Create a network for containers to join.
+ network, err := controller.NewNetwork("simplebridge", "network1", options)
+ if err != nil {
+    return
+ }
+ 
+ // For a new container: create a sandbox instance (providing a unique key).
+ // For linux it is a filesystem path
+ networkPath := "/var/lib/docker/.../4d23e"
+ networkNamespace, err := sandbox.NewSandbox(networkPath)
+ if err != nil {
+    return
+ }
+ 
+ // 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.
+ _, sinfo, err := network.CreateEndpoint("Endpoint1", networkNamespace.Key(), "")
+ if err != nil {
+    return
+ }
+ 
+ // Add interfaces to the namespace.
+ for _, iface := range sinfo.Interfaces {
+     if err := networkNamespace.AddInterface(iface); err != nil {
+     	    return
+     }
+ }
+ 
+ // Set the gateway IP
+ if err := networkNamespace.SetGateway(sinfo.Gateway); err != nil {
+    return
+ }
 ```
 
 ## Future

+ 4 - 3
libnetwork/cmd/test/main.go

@@ -6,15 +6,16 @@ import (
 	"net"
 
 	"github.com/docker/libnetwork"
-	_ "github.com/docker/libnetwork/drivers/bridge"
+	"github.com/docker/libnetwork/pkg/options"
 )
 
 func main() {
 	ip, net, _ := net.ParseCIDR("192.168.100.1/24")
 	net.IP = ip
 
-	options := libnetwork.DriverParams{"AddressIPv4": net}
-	netw, err := libnetwork.NewNetwork("simplebridge", "dummy", options)
+	options := options.Generic{"AddressIPv4": net}
+	controller := libnetwork.New()
+	netw, err := controller.NewNetwork("simplebridge", "dummy", options)
 	if err != nil {
 		log.Fatal(err)
 	}

+ 0 - 81
libnetwork/configure.go

@@ -1,81 +0,0 @@
-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)},
-		{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)},
-		{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 setInterfaceName(iface netlink.Link, settings *Interface) error {
-	return netlink.LinkSetName(iface, settings.DstName)
-}

+ 72 - 0
libnetwork/driverapi/driverapi.go

@@ -0,0 +1,72 @@
+package driverapi
+
+import "errors"
+
+var (
+	// ErrEndpointExists is returned if more than one endpoint is added to the network
+	ErrEndpointExists = errors.New("Endpoint already exists (Only one endpoint allowed)")
+	// ErrNoNetwork is returned if no network with the specified id exists
+	ErrNoNetwork = errors.New("No network exists")
+	// ErrNoEndpoint is returned if no endpoint with the specified id exists
+	ErrNoEndpoint = errors.New("No endpoint exists")
+)
+
+// UUID represents a globally unique ID of various resources like network and endpoint
+type UUID string
+
+// Driver is an interface that every plugin driver needs to implement.
+type Driver interface {
+	// CreateNetwork invokes the driver method to create a network passing
+	// the network id and driver specific config. The config mechanism will
+	// eventually be replaced with labels which are yet to be introduced.
+	CreateNetwork(nid UUID, config interface{}) error
+
+	// DeleteNetwork invokes the driver method to delete network passing
+	// the network id.
+	DeleteNetwork(nid UUID) error
+
+	// CreateEndpoint invokes the driver method to create an endpoint
+	// passing the network id, endpoint id, sandbox key and driver
+	// specific config. The config mechanism will eventually be replaced
+	// with labels which are yet to be introduced.
+	CreateEndpoint(nid, eid UUID, key string, config interface{}) (*SandboxInfo, error)
+
+	// DeleteEndpoint invokes the driver method to delete an endpoint
+	// passing the network id and endpoint id.
+	DeleteEndpoint(nid, eid UUID) error
+}
+
+// Interface represents the settings and identity of a network device. It is
+// used as a return type for Network.Link, and it is common practice for the
+// 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 {
+	// 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
+
+	// IPv4 address for the interface.
+	Address string
+
+	// IPv6 address for the interface.
+	AddressIPv6 string
+}
+
+// SandboxInfo represents all possible information that
+// the driver wants to place in the sandbox which includes
+// interfaces, routes and gateway
+type SandboxInfo struct {
+	Interfaces []*Interface
+
+	// IPv4 gateway for the sandbox.
+	Gateway string
+
+	// IPv6 gateway for the sandbox.
+	GatewayIPv6 string
+
+	// TODO: Add routes and ip tables etc.
+}

+ 9 - 56
libnetwork/drivers.go

@@ -1,65 +1,18 @@
 package libnetwork
 
 import (
-	"fmt"
-
-	"github.com/docker/libnetwork/pkg/options"
+	"github.com/docker/libnetwork/driverapi"
+	"github.com/docker/libnetwork/drivers/bridge"
 )
 
-// DriverParams are a generic structure to hold driver specific settings.
-type DriverParams options.Generic
-
-// DriverInterface is an interface that every plugin driver needs to implement.
-type DriverInterface interface {
-	CreateNetwork(string, interface{}) (Network, error)
-}
-
-var drivers = map[string]struct {
-	creatorFn  DriverInterface
-	creatorArg interface{}
-}{}
-
-// RegisterNetworkType associates a textual identifier with a way to create a
-// new network. It is called by the various network implementations, and used
-// upon invokation of the libnetwork.NetNetwork function.
-//
-// creatorFn must implement DriverInterface
-//
-// For example:
-//
-//    type driver struct{}
-//
-//    func (d *driver) CreateNetwork(name string, config *TestNetworkConfig) (Network, error) {
-//    }
-//
-//    func init() {
-//        RegisterNetworkType("test", &driver{}, &TestNetworkConfig{})
-//    }
-//
-func RegisterNetworkType(name string, creatorFn DriverInterface, creatorArg interface{}) error {
-	// Store the new driver information to invoke at creation time.
-	if _, ok := drivers[name]; ok {
-		return fmt.Errorf("a driver for network type %q is already registed", name)
-	}
-
-	drivers[name] = struct {
-		creatorFn  DriverInterface
-		creatorArg interface{}
-	}{creatorFn, creatorArg}
-
-	return nil
-}
-
-func createNetwork(networkType, name string, generic DriverParams) (Network, error) {
-	d, ok := drivers[networkType]
-	if !ok {
-		return nil, fmt.Errorf("unknown driver %q", networkType)
-	}
+type driverTable map[string]driverapi.Driver
 
-	config, err := options.GenerateFromModel(options.Generic(generic), d.creatorArg)
-	if err != nil {
-		return nil, fmt.Errorf("failed to generate driver config: %v", err)
+func enumerateDrivers() driverTable {
+	var drivers driverTable
+	for _, fn := range [](func() (string, driverapi.Driver)){bridge.New} {
+		name, driver := fn()
+		drivers[name] = driver
 	}
 
-	return d.creatorFn.CreateNetwork(name, config)
+	return drivers
 }

+ 286 - 10
libnetwork/drivers/bridge/bridge.go

@@ -1,12 +1,18 @@
 package bridge
 
 import (
+	"errors"
+	"fmt"
 	"net"
+	"strings"
 	"sync"
 
-	"github.com/docker/libnetwork"
+	"github.com/docker/libcontainer/utils"
+	"github.com/docker/libnetwork/driverapi"
 	"github.com/docker/libnetwork/ipallocator"
+	"github.com/docker/libnetwork/pkg/options"
 	"github.com/docker/libnetwork/portmapper"
+	"github.com/vishvananda/netlink"
 )
 
 const (
@@ -39,23 +45,76 @@ type Configuration struct {
 	EnableIPForwarding bool
 }
 
-type driver struct{}
+type bridgeEndpoint struct {
+	id          driverapi.UUID
+	addressIPv4 net.IP
+	addressIPv6 net.IP
+}
+
+type bridgeNetwork struct {
+	id driverapi.UUID
+	// bridge interface points to the linux bridge and it's configuration
+	bridge   *bridgeInterface
+	endpoint *bridgeEndpoint
+	sync.Mutex
+}
+
+type driver struct {
+	network *bridgeNetwork
+	sync.Mutex
+}
 
 func init() {
 	ipAllocator = ipallocator.New()
 	initPortMapper()
-	libnetwork.RegisterNetworkType(networkType, &driver{}, &Configuration{})
+}
+
+// New provides a new instance of bridge driver instance
+func New() (string, driverapi.Driver) {
+	return networkType, &driver{}
 }
 
 // Create a new network using simplebridge plugin
-func (d *driver) CreateNetwork(name string, opaqueConfig interface{}) (libnetwork.Network, error) {
-	config := opaqueConfig.(*Configuration)
-	bridgeIntfc := newInterface(config)
-	bridgeSetup := newBridgeSetup(bridgeIntfc)
+func (d *driver) CreateNetwork(id driverapi.UUID, option interface{}) error {
+
+	var (
+		config *Configuration
+		err    error
+	)
+
+	d.Lock()
+	if d.network != nil {
+		d.Unlock()
+		return fmt.Errorf("network already exists, simplebridge can only have one network")
+	}
+	d.network = &bridgeNetwork{id: id}
+	d.Unlock()
+	defer func() {
+		// On failure make sure to reset d.network to nil
+		if err != nil {
+			d.Lock()
+			d.network = nil
+			d.Unlock()
+		}
+	}()
+
+	switch opt := option.(type) {
+	case options.Generic:
+		opaqueConfig, err := options.GenerateFromModel(opt, &Configuration{})
+		if err != nil {
+			return fmt.Errorf("failed to generate driver config: %v", err)
+		}
+		config = opaqueConfig.(*Configuration)
+	case *Configuration:
+		config = opt
+	}
+
+	bridgeIface := newInterface(config)
+	bridgeSetup := newBridgeSetup(bridgeIface)
 
 	// If the bridge interface doesn't exist, we need to start the setup steps
 	// by creating a new device and assigning it an IPv4 address.
-	bridgeAlreadyExists := bridgeIntfc.exists()
+	bridgeAlreadyExists := bridgeIface.exists()
 	if !bridgeAlreadyExists {
 		bridgeSetup.queueStep(setupDevice)
 		bridgeSetup.queueStep(setupBridgeIPv4)
@@ -97,9 +156,226 @@ func (d *driver) CreateNetwork(name string, opaqueConfig interface{}) (libnetwor
 
 	// Apply the prepared list of steps, and abort at the first error.
 	bridgeSetup.queueStep(setupDeviceUp)
-	if err := bridgeSetup.apply(); err != nil {
+	if err = bridgeSetup.apply(); err != nil {
+		return err
+	}
+
+	d.network.bridge = bridgeIface
+	return nil
+}
+
+func (d *driver) DeleteNetwork(nid driverapi.UUID) error {
+	var err error
+	d.Lock()
+	n := d.network
+	d.network = nil
+	d.Unlock()
+	defer func() {
+		if err != nil {
+			// On failure set d.network back to n
+			// but only if is not already take over
+			// by some other thread
+			d.Lock()
+			if d.network == nil {
+				d.network = n
+			}
+			d.Unlock()
+		}
+	}()
+
+	if n == nil {
+		err = driverapi.ErrNoNetwork
+		return err
+	}
+
+	if n.endpoint != nil {
+		err = fmt.Errorf("Network %s has active endpoint %s", n.id, n.endpoint.id)
+		return err
+	}
+
+	err = netlink.LinkDel(n.bridge.Link)
+	return err
+}
+
+func (d *driver) CreateEndpoint(nid, eid driverapi.UUID, sboxKey string, config interface{}) (*driverapi.SandboxInfo, error) {
+	var (
+		ipv6Addr net.IPNet
+		err      error
+	)
+
+	d.Lock()
+	n := d.network
+	d.Unlock()
+	if n == nil {
+		return nil, driverapi.ErrNoNetwork
+	}
+
+	n.Lock()
+	if n.id != nid {
+		n.Unlock()
+		return nil, fmt.Errorf("invalid network id %s", nid)
+	}
+
+	if n.endpoint != nil {
+		n.Unlock()
+		return nil, driverapi.ErrEndpointExists
+	}
+	n.endpoint = &bridgeEndpoint{id: eid}
+	n.Unlock()
+	defer func() {
+		// On failye make sure to reset n.endpoint to nil
+		if err != nil {
+			n.Lock()
+			n.endpoint = nil
+			n.Unlock()
+		}
+	}()
+
+	name1, err := generateIfaceName()
+	if err != nil {
+		return nil, err
+	}
+
+	name2, err := generateIfaceName()
+	if err != nil {
+		return nil, err
+	}
+
+	veth := &netlink.Veth{
+		LinkAttrs: netlink.LinkAttrs{Name: name1, TxQLen: 0},
+		PeerName:  name2}
+	if err = netlink.LinkAdd(veth); err != nil {
+		return nil, err
+	}
+
+	host, err := netlink.LinkByName(name1)
+	if err != nil {
+		return nil, err
+	}
+	defer func() {
+		if err != nil {
+			netlink.LinkDel(host)
+		}
+	}()
+
+	container, err := netlink.LinkByName(name2)
+	if err != nil {
 		return nil, err
 	}
+	defer func() {
+		if err != nil {
+			netlink.LinkDel(container)
+		}
+	}()
+
+	if err = netlink.LinkSetMaster(host,
+		&netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: n.bridge.Config.BridgeName}}); err != nil {
+		return nil, err
+	}
+
+	ip4, err := ipAllocator.RequestIP(n.bridge.bridgeIPv4, nil)
+	if err != nil {
+		return nil, err
+	}
+	ipv4Addr := net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask}
+
+	if n.bridge.Config.EnableIPv6 {
+		ip6, err := ipAllocator.RequestIP(n.bridge.bridgeIPv6, nil)
+		if err != nil {
+			return nil, err
+		}
+		ipv6Addr = net.IPNet{IP: ip6, Mask: n.bridge.bridgeIPv6.Mask}
+	}
+
+	var interfaces []*driverapi.Interface
+	sinfo := &driverapi.SandboxInfo{}
+
+	intf := &driverapi.Interface{}
+	intf.SrcName = name2
+	intf.DstName = "eth0"
+	intf.Address = ipv4Addr.String()
+	sinfo.Gateway = n.bridge.bridgeIPv4.IP.String()
+	if n.bridge.Config.EnableIPv6 {
+		intf.AddressIPv6 = ipv6Addr.String()
+		sinfo.GatewayIPv6 = n.bridge.bridgeIPv6.IP.String()
+	}
+
+	n.endpoint.addressIPv4 = ip4
+	n.endpoint.addressIPv6 = ipv6Addr.IP
+	interfaces = append(interfaces, intf)
+	sinfo.Interfaces = interfaces
+	return sinfo, nil
+}
+
+func (d *driver) DeleteEndpoint(nid, eid driverapi.UUID) error {
+	var err error
+
+	d.Lock()
+	n := d.network
+	d.Unlock()
+	if n == nil {
+		return driverapi.ErrNoNetwork
+	}
+
+	n.Lock()
+	if n.id != nid {
+		n.Unlock()
+		return fmt.Errorf("invalid network id %s", nid)
+	}
+
+	if n.endpoint == nil {
+		n.Unlock()
+		return driverapi.ErrNoEndpoint
+	}
+
+	ep := n.endpoint
+	if ep.id != eid {
+		n.Unlock()
+		return fmt.Errorf("invalid endpoint id %s", eid)
+	}
+
+	n.endpoint = nil
+	n.Unlock()
+	defer func() {
+		if err != nil {
+			// On failure make to set back n.endpoint with ep
+			// but only if it hasn't been taken over
+			// already by some other thread.
+			n.Lock()
+			if n.endpoint == nil {
+				n.endpoint = ep
+			}
+			n.Unlock()
+		}
+	}()
 
-	return &bridgeNetwork{NetworkName: name, bridge: bridgeIntfc}, nil
+	err = ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, ep.addressIPv4)
+	if err != nil {
+		return err
+	}
+
+	if n.bridge.Config.EnableIPv6 {
+		err := ipAllocator.ReleaseIP(n.bridge.bridgeIPv6, n.endpoint.addressIPv6)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func generateIfaceName() (string, error) {
+	for i := 0; i < 10; i++ {
+		name, err := utils.GenerateRandomName("veth", 7)
+		if err != nil {
+			continue
+		}
+		if _, err := net.InterfaceByName(name); err != nil {
+			if strings.Contains(err.Error(), "no such") {
+				return name, nil
+			}
+			return "", err
+		}
+	}
+	return "", errors.New("Failed to find name for new interface")
 }

+ 10 - 18
libnetwork/drivers/bridge/bridge_test.go

@@ -4,37 +4,33 @@ import (
 	"net"
 	"testing"
 
-	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
 )
 
 func TestCreate(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
-	d := &driver{}
+	defer netutils.SetupTestNetNS(t)()
+	_, d := New()
 
 	config := &Configuration{BridgeName: DefaultBridgeName}
-	netw, err := d.CreateNetwork("dummy", config)
+	err := d.CreateNetwork("dummy", config)
 	if err != nil {
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
-
-	if expected := networkType; netw.Type() != expected {
-		t.Fatalf("Expected networkType %q, got %q", expected, netw.Type())
-	}
 }
 
 func TestCreateFail(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
-	d := &driver{}
+	defer netutils.SetupTestNetNS(t)()
+	_, d := New()
 
 	config := &Configuration{BridgeName: "dummy0"}
-	if _, err := d.CreateNetwork("dummy", config); err == nil {
+	if err := d.CreateNetwork("dummy", config); err == nil {
 		t.Fatal("Bridge creation was expected to fail")
 	}
 }
 
 func TestCreateFullOptions(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
-	d := &driver{}
+	defer netutils.SetupTestNetNS(t)()
+	_, d := New()
 
 	config := &Configuration{
 		BridgeName:         DefaultBridgeName,
@@ -45,12 +41,8 @@ func TestCreateFullOptions(t *testing.T) {
 	}
 	_, config.FixedCIDRv6, _ = net.ParseCIDR("2001:db8::/48")
 
-	netw, err := d.CreateNetwork("dummy", config)
+	err := d.CreateNetwork("dummy", config)
 	if err != nil {
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
-
-	if expected := networkType; netw.Type() != expected {
-		t.Fatalf("Expected networkType %q, got %q", expected, netw.Type())
-	}
 }

+ 3 - 3
libnetwork/drivers/bridge/interface_test.go

@@ -3,12 +3,12 @@ package bridge
 import (
 	"testing"
 
-	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
 	"github.com/vishvananda/netlink"
 )
 
 func TestInterfaceDefaultName(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	if inf := newInterface(&Configuration{}); inf.Config.BridgeName != DefaultBridgeName {
 		t.Fatalf("Expected default interface name %q, got %q", DefaultBridgeName, inf.Config.BridgeName)
@@ -16,7 +16,7 @@ func TestInterfaceDefaultName(t *testing.T) {
 }
 
 func TestAddressesEmptyInterface(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	inf := newInterface(&Configuration{})
 	addrv4, addrsv6, err := inf.addresses()

+ 0 - 128
libnetwork/drivers/bridge/network.go

@@ -1,128 +0,0 @@
-package bridge
-
-import (
-	"errors"
-	"net"
-	"strings"
-
-	"github.com/docker/libcontainer/utils"
-	"github.com/docker/libnetwork"
-	"github.com/vishvananda/netlink"
-)
-
-// ErrEndpointExists is returned if more than one endpoint is added to the network
-var ErrEndpointExists = errors.New("Endpoint already exists (Only one endpoint allowed)")
-
-type bridgeNetwork struct {
-	NetworkName string
-	bridge      *bridgeInterface
-	EndPoint    *libnetwork.Interface
-}
-
-func (b *bridgeNetwork) Name() string {
-	return b.NetworkName
-}
-
-func (b *bridgeNetwork) Type() string {
-	return networkType
-}
-
-func (b *bridgeNetwork) Link(name string) ([]*libnetwork.Interface, error) {
-
-	var ipv6Addr net.IPNet
-
-	if b.EndPoint != nil {
-		return nil, ErrEndpointExists
-	}
-
-	name1, err := generateIfaceName()
-	if err != nil {
-		return nil, err
-	}
-
-	name2, err := generateIfaceName()
-	if err != nil {
-		return nil, err
-	}
-
-	veth := &netlink.Veth{
-		LinkAttrs: netlink.LinkAttrs{Name: name1, TxQLen: 0},
-		PeerName:  name2}
-	if err := netlink.LinkAdd(veth); err != nil {
-		return nil, err
-	}
-
-	host, err := netlink.LinkByName(name1)
-	if err != nil {
-		return nil, err
-	}
-	defer func() {
-		if err != nil {
-			netlink.LinkDel(host)
-		}
-	}()
-
-	container, err := netlink.LinkByName(name2)
-	if err != nil {
-		return nil, err
-	}
-	defer func() {
-		if err != nil {
-			netlink.LinkDel(container)
-		}
-	}()
-
-	if err = netlink.LinkSetMaster(host,
-		&netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: b.bridge.Config.BridgeName}}); err != nil {
-		return nil, err
-	}
-
-	ip4, err := ipAllocator.RequestIP(b.bridge.bridgeIPv4, nil)
-	if err != nil {
-		return nil, err
-	}
-	ipv4Addr := net.IPNet{IP: ip4, Mask: b.bridge.bridgeIPv4.Mask}
-
-	if b.bridge.Config.EnableIPv6 {
-		ip6, err := ipAllocator.RequestIP(b.bridge.bridgeIPv6, nil)
-		if err != nil {
-			return nil, err
-		}
-		ipv6Addr = net.IPNet{IP: ip6, Mask: b.bridge.bridgeIPv6.Mask}
-	}
-
-	var interfaces []*libnetwork.Interface
-	intf := &libnetwork.Interface{}
-	intf.SrcName = name2
-	intf.DstName = "eth0"
-	intf.Address = ipv4Addr.String()
-	intf.Gateway = b.bridge.bridgeIPv4.IP.String()
-	if b.bridge.Config.EnableIPv6 {
-		intf.AddressIPv6 = ipv6Addr.String()
-		intf.GatewayIPv6 = b.bridge.bridgeIPv6.IP.String()
-	}
-
-	b.EndPoint = intf
-	interfaces = append(interfaces, intf)
-	return interfaces, nil
-}
-
-func generateIfaceName() (string, error) {
-	for i := 0; i < 10; i++ {
-		name, err := utils.GenerateRandomName("veth", 7)
-		if err != nil {
-			continue
-		}
-		if _, err := net.InterfaceByName(name); err != nil {
-			if strings.Contains(err.Error(), "no such") {
-				return name, nil
-			}
-			return "", err
-		}
-	}
-	return "", errors.New("Failed to find name for new interface")
-}
-
-func (b *bridgeNetwork) Delete() error {
-	return netlink.LinkDel(b.bridge.Link)
-}

+ 31 - 27
libnetwork/drivers/bridge/network_test.go

@@ -4,27 +4,30 @@ import (
 	"net"
 	"testing"
 
-	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/driverapi"
+	"github.com/docker/libnetwork/netutils"
 	"github.com/vishvananda/netlink"
 )
 
 func TestLinkCreate(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
-	d := &driver{}
+	defer netutils.SetupTestNetNS(t)()
+	_, d := New()
+	dr := d.(*driver)
 
 	config := &Configuration{
 		BridgeName: DefaultBridgeName,
 		EnableIPv6: true}
-	netw, err := d.CreateNetwork("dummy", config)
+	err := d.CreateNetwork("dummy", config)
 	if err != nil {
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 
-	interfaces, err := netw.Link("ep")
+	sinfo, err := d.CreateEndpoint("dummy", "ep", "", "")
 	if err != nil {
 		t.Fatalf("Failed to create a link: %v", err)
 	}
 
+	interfaces := sinfo.Interfaces
 	if len(interfaces) != 1 {
 		t.Fatalf("Expected exactly one interface. Instead got %d interface(s)", len(interfaces))
 	}
@@ -43,9 +46,9 @@ func TestLinkCreate(t *testing.T) {
 		t.Fatalf("Invalid IPv4 address returned, ip = %s: %v", interfaces[0].Address, err)
 	}
 
-	b := netw.(*bridgeNetwork)
-	if !b.bridge.bridgeIPv4.Contains(ip) {
-		t.Fatalf("IP %s is not a valid ip in the subnet %s", ip.String(), b.bridge.bridgeIPv4.String())
+	n := dr.network
+	if !n.bridge.bridgeIPv4.Contains(ip) {
+		t.Fatalf("IP %s is not a valid ip in the subnet %s", ip.String(), n.bridge.bridgeIPv4.String())
 	}
 
 	ip6, _, err := net.ParseCIDR(interfaces[0].AddressIPv6)
@@ -53,41 +56,41 @@ func TestLinkCreate(t *testing.T) {
 		t.Fatalf("Invalid IPv6 address returned, ip = %s: %v", interfaces[0].AddressIPv6, err)
 	}
 
-	if !b.bridge.bridgeIPv6.Contains(ip6) {
+	if !n.bridge.bridgeIPv6.Contains(ip6) {
 		t.Fatalf("IP %s is not a valid ip in the subnet %s", ip6.String(), bridgeIPv6.String())
 	}
 
-	if interfaces[0].Gateway != b.bridge.bridgeIPv4.IP.String() {
-		t.Fatalf("Invalid default gateway. Expected %s. Got %s", b.bridge.bridgeIPv4.IP.String(),
-			interfaces[0].Gateway)
+	if sinfo.Gateway != n.bridge.bridgeIPv4.IP.String() {
+		t.Fatalf("Invalid default gateway. Expected %s. Got %s", n.bridge.bridgeIPv4.IP.String(),
+			sinfo.Gateway)
 	}
 
-	if interfaces[0].GatewayIPv6 != b.bridge.bridgeIPv6.IP.String() {
-		t.Fatalf("Invalid default gateway for IPv6. Expected %s. Got %s", b.bridge.bridgeIPv6.IP.String(),
-			interfaces[0].GatewayIPv6)
+	if sinfo.GatewayIPv6 != n.bridge.bridgeIPv6.IP.String() {
+		t.Fatalf("Invalid default gateway for IPv6. Expected %s. Got %s", n.bridge.bridgeIPv6.IP.String(),
+			sinfo.GatewayIPv6)
 	}
 }
 
 func TestLinkCreateTwo(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
-	d := &driver{}
+	defer netutils.SetupTestNetNS(t)()
+	_, d := New()
 
 	config := &Configuration{
 		BridgeName: DefaultBridgeName,
 		EnableIPv6: true}
-	netw, err := d.CreateNetwork("dummy", config)
+	err := d.CreateNetwork("dummy", config)
 	if err != nil {
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 
-	_, err = netw.Link("ep")
+	_, err = d.CreateEndpoint("dummy", "ep", "", "")
 	if err != nil {
 		t.Fatalf("Failed to create a link: %v", err)
 	}
 
-	_, err = netw.Link("ep1")
+	_, err = d.CreateEndpoint("dummy", "ep1", "", "")
 	if err != nil {
-		if err != ErrEndpointExists {
+		if err != driverapi.ErrEndpointExists {
 			t.Fatalf("Failed with a wrong error :%v", err)
 		}
 	} else {
@@ -96,24 +99,25 @@ func TestLinkCreateTwo(t *testing.T) {
 }
 
 func TestLinkCreateNoEnableIPv6(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
-	d := &driver{}
+	defer netutils.SetupTestNetNS(t)()
+	_, d := New()
 
 	config := &Configuration{
 		BridgeName: DefaultBridgeName}
-	netw, err := d.CreateNetwork("dummy", config)
+	err := d.CreateNetwork("dummy", config)
 	if err != nil {
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 
-	interfaces, err := netw.Link("ep")
+	sinfo, err := d.CreateEndpoint("dummy", "ep", "", "")
 	if err != nil {
 		t.Fatalf("Failed to create a link: %v", err)
 	}
 
+	interfaces := sinfo.Interfaces
 	if interfaces[0].AddressIPv6 != "" ||
-		interfaces[0].GatewayIPv6 != "" {
+		sinfo.GatewayIPv6 != "" {
 		t.Fatalf("Expected IPv6 address and GatewayIPv6 to be empty when IPv6 enabled. Instead got IPv6 = %s and GatewayIPv6 = %s",
-			interfaces[0].AddressIPv6, interfaces[0].GatewayIPv6)
+			interfaces[0].AddressIPv6, sinfo.GatewayIPv6)
 	}
 }

+ 2 - 2
libnetwork/drivers/bridge/setup_device.go

@@ -5,7 +5,7 @@ import (
 
 	log "github.com/Sirupsen/logrus"
 	"github.com/docker/docker/pkg/parsers/kernel"
-	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
 	"github.com/vishvananda/netlink"
 )
 
@@ -28,7 +28,7 @@ func setupDevice(i *bridgeInterface) error {
 	// was not supported before that.
 	kv, err := kernel.GetKernelVersion()
 	if err == nil && (kv.Kernel >= 3 && kv.Major >= 3) {
-		i.Link.Attrs().HardwareAddr = libnetwork.GenerateRandomMAC()
+		i.Link.Attrs().HardwareAddr = netutils.GenerateRandomMAC()
 		log.Debugf("Setting bridge mac address to %s", i.Link.Attrs().HardwareAddr)
 	}
 

+ 7 - 7
libnetwork/drivers/bridge/setup_device_test.go

@@ -6,12 +6,12 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
 	"github.com/vishvananda/netlink"
 )
 
 func TestSetupNewBridge(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	br := &bridgeInterface{
 		Config: &Configuration{
@@ -33,7 +33,7 @@ func TestSetupNewBridge(t *testing.T) {
 }
 
 func TestSetupNewNonDefaultBridge(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	br := &bridgeInterface{
 		Config: &Configuration{
@@ -46,7 +46,7 @@ func TestSetupNewNonDefaultBridge(t *testing.T) {
 }
 
 func TestSetupDeviceUp(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	br := &bridgeInterface{
 		Config: &Configuration{
@@ -67,10 +67,10 @@ func TestSetupDeviceUp(t *testing.T) {
 }
 
 func TestGenerateRandomMAC(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
-	mac1 := libnetwork.GenerateRandomMAC()
-	mac2 := libnetwork.GenerateRandomMAC()
+	mac1 := netutils.GenerateRandomMAC()
+	mac2 := netutils.GenerateRandomMAC()
 	if bytes.Compare(mac1, mac2) == 0 {
 		t.Fatalf("Generated twice the same MAC address %v", mac1)
 	}

+ 3 - 3
libnetwork/drivers/bridge/setup_fixedcidrv4_test.go

@@ -4,11 +4,11 @@ import (
 	"net"
 	"testing"
 
-	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
 )
 
 func TestSetupFixedCIDRv4(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	br := &bridgeInterface{
 		Config: &Configuration{
@@ -36,7 +36,7 @@ func TestSetupFixedCIDRv4(t *testing.T) {
 }
 
 func TestSetupBadFixedCIDRv4(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	br := &bridgeInterface{
 		Config: &Configuration{

+ 2 - 2
libnetwork/drivers/bridge/setup_fixedcidrv6_test.go

@@ -4,11 +4,11 @@ import (
 	"net"
 	"testing"
 
-	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
 )
 
 func TestSetupFixedCIDRv6(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	br := newInterface(&Configuration{})
 

+ 2 - 2
libnetwork/drivers/bridge/setup_ip_tables.go

@@ -5,7 +5,7 @@ import (
 	"net"
 
 	"github.com/docker/docker/pkg/iptables"
-	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
 )
 
 // DockerChain: DOCKER iptable chain name
@@ -19,7 +19,7 @@ func setupIPTables(i *bridgeInterface) error {
 		return fmt.Errorf("Unexpected request to set IP tables for interface: %s", i.Config.BridgeName)
 	}
 
-	addrv4, _, err := libnetwork.GetIfaceAddr(i.Config.BridgeName)
+	addrv4, _, err := netutils.GetIfaceAddr(i.Config.BridgeName)
 	if err != nil {
 		return fmt.Errorf("Failed to setup IP tables, cannot acquire Interface address: %s", err.Error())
 	}

+ 3 - 3
libnetwork/drivers/bridge/setup_ip_tables_test.go

@@ -5,7 +5,7 @@ import (
 	"testing"
 
 	"github.com/docker/docker/pkg/iptables"
-	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
 )
 
 const (
@@ -14,7 +14,7 @@ const (
 
 func TestProgramIPTable(t *testing.T) {
 	// Create a test bridge with a basic bridge configuration (name + IPv4).
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 	createTestBridge(getBasicTestConfig(), t)
 
 	// Store various iptables chain rules we care for.
@@ -38,7 +38,7 @@ func TestProgramIPTable(t *testing.T) {
 
 func TestSetupIPTables(t *testing.T) {
 	// Create a test bridge with a basic bridge configuration (name + IPv4).
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 	br := getBasicTestConfig()
 	createTestBridge(br, t)
 

+ 3 - 3
libnetwork/drivers/bridge/setup_ipv4.go

@@ -5,7 +5,7 @@ import (
 	"net"
 
 	log "github.com/Sirupsen/logrus"
-	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
 	"github.com/vishvananda/netlink"
 )
 
@@ -73,8 +73,8 @@ func electBridgeIPv4(config *Configuration) (*net.IPNet, error) {
 
 	// Try to automatically elect appropriate brige IPv4 settings.
 	for _, n := range bridgeNetworks {
-		if err := libnetwork.CheckNameserverOverlaps(nameservers, n); err == nil {
-			if err := libnetwork.CheckRouteOverlaps(n); err == nil {
+		if err := netutils.CheckNameserverOverlaps(nameservers, n); err == nil {
+			if err := netutils.CheckRouteOverlaps(n); err == nil {
 				return n, nil
 			}
 		}

+ 3 - 3
libnetwork/drivers/bridge/setup_ipv4_test.go

@@ -4,7 +4,7 @@ import (
 	"net"
 	"testing"
 
-	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
 	"github.com/vishvananda/netlink"
 )
 
@@ -21,7 +21,7 @@ func setupTestInterface(t *testing.T) *bridgeInterface {
 }
 
 func TestSetupBridgeIPv4Fixed(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	ip, netw, err := net.ParseCIDR("192.168.1.1/24")
 	if err != nil {
@@ -53,7 +53,7 @@ func TestSetupBridgeIPv4Fixed(t *testing.T) {
 }
 
 func TestSetupBridgeIPv4Auto(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	br := setupTestInterface(t)
 	if err := setupBridgeIPv4(br); err != nil {

+ 2 - 2
libnetwork/drivers/bridge/setup_ipv6_test.go

@@ -6,12 +6,12 @@ import (
 	"io/ioutil"
 	"testing"
 
-	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
 	"github.com/vishvananda/netlink"
 )
 
 func TestSetupIPv6(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	br := setupTestInterface(t)
 	if err := setupBridgeIPv6(br); err != nil {

+ 6 - 6
libnetwork/drivers/bridge/setup_verify_test.go

@@ -4,7 +4,7 @@ import (
 	"net"
 	"testing"
 
-	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
 	"github.com/vishvananda/netlink"
 )
 
@@ -23,7 +23,7 @@ func setupVerifyTest(t *testing.T) *bridgeInterface {
 }
 
 func TestSetupVerify(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	addrv4 := net.IPv4(192, 168, 1, 1)
 	inf := setupVerifyTest(t)
@@ -39,7 +39,7 @@ func TestSetupVerify(t *testing.T) {
 }
 
 func TestSetupVerifyBad(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	addrv4 := net.IPv4(192, 168, 1, 1)
 	inf := setupVerifyTest(t)
@@ -56,7 +56,7 @@ func TestSetupVerifyBad(t *testing.T) {
 }
 
 func TestSetupVerifyMissing(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	addrv4 := net.IPv4(192, 168, 1, 1)
 	inf := setupVerifyTest(t)
@@ -68,7 +68,7 @@ func TestSetupVerifyMissing(t *testing.T) {
 }
 
 func TestSetupVerifyIPv6(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	addrv4 := net.IPv4(192, 168, 1, 1)
 	inf := setupVerifyTest(t)
@@ -88,7 +88,7 @@ func TestSetupVerifyIPv6(t *testing.T) {
 }
 
 func TestSetupVerifyIPv6Missing(t *testing.T) {
-	defer libnetwork.SetupTestNetNS(t)()
+	defer netutils.SetupTestNetNS(t)()
 
 	addrv4 := net.IPv4(192, 168, 1, 1)
 	inf := setupVerifyTest(t)

+ 3 - 3
libnetwork/ipallocator/allocator.go

@@ -9,7 +9,7 @@ import (
 	"sync"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/libnetwork"
+	"github.com/docker/libnetwork/netutils"
 )
 
 // allocatedMap is thread-unsafe set of allocated IP
@@ -21,7 +21,7 @@ type allocatedMap struct {
 }
 
 func newAllocatedMap(network *net.IPNet) *allocatedMap {
-	firstIP, lastIP := libnetwork.NetworkRange(network)
+	firstIP, lastIP := netutils.NetworkRange(network)
 	begin := big.NewInt(0).Add(ipToBigInt(firstIP), big.NewInt(1))
 	end := big.NewInt(0).Sub(ipToBigInt(lastIP), big.NewInt(1))
 
@@ -71,7 +71,7 @@ func (a *IPAllocator) RegisterSubnet(network *net.IPNet, subnet *net.IPNet) erro
 		return ErrNetworkAlreadyRegistered
 	}
 	n := newAllocatedMap(network)
-	beginIP, endIP := libnetwork.NetworkRange(subnet)
+	beginIP, endIP := netutils.NetworkRange(subnet)
 	begin := big.NewInt(0).Add(ipToBigInt(beginIP), big.NewInt(1))
 	end := big.NewInt(0).Sub(ipToBigInt(endIP), big.NewInt(1))
 

+ 5 - 2
libnetwork/libnetwork_test.go

@@ -8,6 +8,7 @@ import (
 	log "github.com/Sirupsen/logrus"
 	"github.com/docker/libnetwork"
 	_ "github.com/docker/libnetwork/drivers/bridge"
+	"github.com/docker/libnetwork/pkg/options"
 	"github.com/vishvananda/netlink"
 )
 
@@ -41,7 +42,7 @@ func TestSimplebridge(t *testing.T) {
 	cidrv6.IP = ip
 
 	log.Debug("Adding a simple bridge")
-	options := libnetwork.DriverParams{
+	options := options.Generic{
 		"BridgeName":         bridgeName,
 		"AddressIPv4":        subnet,
 		"FixedCIDR":          cidr,
@@ -52,7 +53,9 @@ func TestSimplebridge(t *testing.T) {
 		"EnableICC":          true,
 		"EnableIPForwarding": true}
 
-	network, err := libnetwork.NewNetwork("simplebridge", "dummy", options)
+	controller := libnetwork.New()
+
+	network, err := controller.NewNetwork("simplebridge", "dummy", options)
 	if err != nil {
 		t.Fatal(err)
 	}

+ 1 - 1
libnetwork/testing.go → libnetwork/netutils/test_utils.go

@@ -1,4 +1,4 @@
-package libnetwork
+package netutils
 
 import (
 	"runtime"

+ 1 - 1
libnetwork/utils.go → libnetwork/netutils/utils.go

@@ -1,7 +1,7 @@
 // Network utility functions.
 // Imported unchanged from Docker
 
-package libnetwork
+package netutils
 
 import (
 	"errors"

+ 1 - 1
libnetwork/utils_test.go → libnetwork/netutils/utils_test.go

@@ -1,4 +1,4 @@
-package libnetwork
+package netutils
 
 import (
 	"net"

+ 209 - 74
libnetwork/network.go

@@ -1,60 +1,61 @@
-// 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.NewNetworkNamespace(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.Link(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
+/*
+Package libnetwork provides basic fonctionalities and extension points to
+create network namespaces and allocate interfaces for containers to use.
+
+// Create a new controller instance
+controller := libnetwork.New()
+
+options := options.Generic{}
+// Create a network for containers to join.
+network, err := controller.NewNetwork("simplebridge", "network1", options)
+if err != nil {
+	return
+}
+
+// For a new container: create a sandbox instance (providing a unique key).
+// For linux it is a filesystem path
+networkPath := "/var/lib/docker/.../4d23e"
+networkNamespace, err := sandbox.NewSandbox(networkPath)
+if err != nil {
+	return
+}
 
-// Interface represents the settings and identity of a network device. It is
-// used as a return type for Network.Link, and it is common practice for the
-// 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 {
-	// The name of the interface in the origin network namespace.
-	SrcName string
+// 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.
+_, sinfo, err := network.CreateEndpoint("Endpoint1", networkNamespace.Key(), "")
+if err != nil {
+	return
+}
 
-	// The name that will be assigned to the interface once moves inside a
-	// network namespace.
-	DstName string
+// Add interfaces to the namespace.
+for _, iface := range sinfo.Interfaces {
+	if err := networkNamespace.AddInterface(iface); err != nil {
+		return
+	}
+}
 
-	// IPv4 address for the interface.
-	Address string
+// Set the gateway IP
+if err := networkNamespace.SetGateway(sinfo.Gateway); err != nil {
+	return
+}
+*/
+package libnetwork
 
-	// IPv6 address for the interface.
-	AddressIPv6 string
+import (
+	"fmt"
+	"sync"
 
-	// IPv4 gateway for the interface.
-	Gateway string
+	"github.com/docker/docker/pkg/common"
+	"github.com/docker/libnetwork/driverapi"
+)
 
-	// IPv6 gateway for the interface.
-	GatewayIPv6 string
+// NetworkController provides the interface for controller instance which manages
+// networks.
+type NetworkController interface {
+	// Create a new network. The options parameter carry driver specific options.
+	// Labels support will be added in the near future.
+	NewNetwork(networkType, name string, options interface{}) (Network, error)
 }
 
 // A Network represents a logical connectivity zone that containers may
@@ -64,42 +65,176 @@ type Network interface {
 	// A user chosen name for this network.
 	Name() string
 
+	// A system generated id for this network.
+	ID() string
+
 	// The type of network, which corresponds to its managing driver.
 	Type() string
 
-	// Create a new link to this network symbolically identified by the
-	// specified unique name.
-	Link(name string) ([]*Interface, error)
+	// Create a new endpoint to this network symbolically identified by the
+	// specified unique name. The options parameter carry driver specific options.
+	// Labels support will be added in the near future.
+	CreateEndpoint(name string, sboxKey string, options interface{}) (Endpoint, *driverapi.SandboxInfo, error)
 
+	// Delete the network.
 	Delete() error
 }
 
-// Namespace represents a network namespace, mounted on a specific Path.  It
-// holds a list of Interface, and more can be added dynamically.
-type Namespace interface {
-	// The path where the network namespace is mounted.
-	Path() string
+// Endpoint represents a logical connection between a network and a sandbox.
+type Endpoint interface {
+	// Delete endpoint.
+	Delete() error
+}
+
+type endpoint struct {
+	name        string
+	id          driverapi.UUID
+	network     *network
+	sandboxInfo *driverapi.SandboxInfo
+}
 
-	// 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 existing in any newly
-	// created network namespace).
-	Interfaces() []*Interface
+type network struct {
+	ctrlr       *controller
+	name        string
+	networkType string
+	id          driverapi.UUID
+	endpoints   map[driverapi.UUID]*endpoint
+	sync.Mutex
+}
+
+type networkTable map[driverapi.UUID]*network
 
-	// Add an existing Interface to this namespace. The operation will rename
-	// from the Interface SrcName to DstName as it moves, and reconfigure the
-	// interface according to the specified settings.
-	AddInterface(*Interface) error
+type controller struct {
+	networks networkTable
+	drivers  driverTable
+	sync.Mutex
+}
+
+// New creates a new instance of network controller.
+func New() NetworkController {
+	return &controller{networkTable{}, enumerateDrivers(), sync.Mutex{}}
 }
 
 // NewNetwork creates a new network of the specified networkType. The options
 // are driver specific and modeled in a generic way.
-func NewNetwork(networkType, name string, options DriverParams) (Network, error) {
-	return createNetwork(networkType, name, options)
+func (c *controller) NewNetwork(networkType, name string, options interface{}) (Network, error) {
+	network := &network{name: name, networkType: networkType}
+	network.id = driverapi.UUID(common.GenerateRandomID())
+	network.ctrlr = c
+
+	d, ok := c.drivers[networkType]
+	if !ok {
+		return nil, fmt.Errorf("unknown driver %q", networkType)
+	}
+
+	if err := d.CreateNetwork(network.id, options); err != nil {
+		return nil, err
+	}
+
+	c.Lock()
+	c.networks[network.id] = network
+	c.Unlock()
+
+	return network, nil
 }
 
-// NewNetworkNamespace creates a new network namespace mounted on the specified
-// path.
-func NewNetworkNamespace(path string) (Namespace, error) {
-	return createNetworkNamespace(path)
+func (n *network) Name() string {
+	return n.name
+}
+
+func (n *network) ID() string {
+	return string(n.id)
+}
+
+func (n *network) Type() string {
+	return n.networkType
+}
+
+func (n *network) Delete() error {
+	var err error
+
+	d, ok := n.ctrlr.drivers[n.networkType]
+	if !ok {
+		return fmt.Errorf("unknown driver %q", n.networkType)
+	}
+
+	n.ctrlr.Lock()
+	_, ok = n.ctrlr.networks[n.id]
+	if !ok {
+		n.ctrlr.Unlock()
+		return fmt.Errorf("unknown network %s id %s", n.name, n.id)
+	}
+
+	n.Lock()
+	numEps := len(n.endpoints)
+	n.Unlock()
+	if numEps != 0 {
+		n.ctrlr.Unlock()
+		return fmt.Errorf("network %s has active endpoints", n.id)
+	}
+
+	delete(n.ctrlr.networks, n.id)
+	n.ctrlr.Unlock()
+	defer func() {
+		if err != nil {
+			n.ctrlr.Lock()
+			n.ctrlr.networks[n.id] = n
+			n.ctrlr.Unlock()
+		}
+	}()
+
+	err = d.DeleteNetwork(n.id)
+	return err
+}
+
+func (n *network) CreateEndpoint(name string, sboxKey string, options interface{}) (Endpoint, *driverapi.SandboxInfo, error) {
+	ep := &endpoint{name: name}
+	ep.id = driverapi.UUID(common.GenerateRandomID())
+	ep.network = n
+
+	d, ok := n.ctrlr.drivers[n.networkType]
+	if !ok {
+		return nil, nil, fmt.Errorf("unknown driver %q", n.networkType)
+	}
+
+	sinfo, err := d.CreateEndpoint(n.id, ep.id, sboxKey, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ep.sandboxInfo = sinfo
+	n.Lock()
+	n.endpoints[ep.id] = ep
+	n.Unlock()
+	return ep, sinfo, nil
+}
+
+func (ep *endpoint) Delete() error {
+	var err error
+
+	d, ok := ep.network.ctrlr.drivers[ep.network.networkType]
+	if !ok {
+		return fmt.Errorf("unknown driver %q", ep.network.networkType)
+	}
+
+	n := ep.network
+	n.Lock()
+	_, ok = n.endpoints[ep.id]
+	if !ok {
+		n.Unlock()
+		return fmt.Errorf("unknown endpoint %s id %s", ep.name, ep.id)
+	}
+
+	delete(n.endpoints, ep.id)
+	n.Unlock()
+	defer func() {
+		if err != nil {
+			n.Lock()
+			n.endpoints[ep.id] = ep
+			n.Unlock()
+		}
+	}()
+
+	err = d.DeleteEndpoint(n.id, ep.id)
+	return err
 }

+ 66 - 0
libnetwork/sandbox/configure_linux.go

@@ -0,0 +1,66 @@
+package sandbox
+
+import (
+	"fmt"
+	"net"
+
+	"github.com/docker/libnetwork/driverapi"
+	"github.com/vishvananda/netlink"
+)
+
+func configureInterface(iface netlink.Link, settings *driverapi.Interface) error {
+	ifaceName := iface.Attrs().Name
+	ifaceConfigurators := []struct {
+		Fn         func(netlink.Link, *driverapi.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)},
+		/*		{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(gw string) error {
+	ip := net.ParseIP(gw)
+	if ip == nil {
+		return fmt.Errorf("bad address format %q", gw)
+	}
+
+	return netlink.RouteAdd(&netlink.Route{
+		Scope: netlink.SCOPE_UNIVERSE,
+		Gw:    ip,
+	})
+}
+
+func setInterfaceIP(iface netlink.Link, settings *driverapi.Interface) error {
+	ipAddr, err := netlink.ParseAddr(settings.Address)
+	if err == nil {
+		err = netlink.AddrAdd(iface, ipAddr)
+	}
+	return err
+}
+
+func setInterfaceIPv6(iface netlink.Link, settings *driverapi.Interface) error {
+	if settings.AddressIPv6 == "" {
+		return nil
+	}
+
+	ipAddr, err := netlink.ParseAddr(settings.AddressIPv6)
+	if err == nil {
+		err = netlink.AddrAdd(iface, ipAddr)
+	}
+	return err
+}
+
+func setInterfaceName(iface netlink.Link, settings *driverapi.Interface) error {
+	return netlink.LinkSetName(iface, settings.DstName)
+}

+ 37 - 12
libnetwork/namespace.go → libnetwork/sandbox/namespace_linux.go

@@ -1,4 +1,4 @@
-package libnetwork
+package sandbox
 
 import (
 	"fmt"
@@ -6,19 +6,26 @@ import (
 	"runtime"
 	"syscall"
 
+	"github.com/docker/libnetwork/driverapi"
 	"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
-// into it when called on method AddInterface.
+// The networkNamespace type is the linux implementation of the Sandbox
+// 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
-	interfaces []*Interface
+	path  string
+	sinfo *driverapi.SandboxInfo
 }
 
-func createNetworkNamespace(path string) (Namespace, error) {
+// NewSandbox provides a new sandbox instance created in an os specific way
+// provided a key which uniquely identifies the sandbox
+func NewSandbox(key string) (Sandbox, error) {
+	return createNetworkNamespace(key)
+}
+
+func createNetworkNamespace(path string) (Sandbox, error) {
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
@@ -66,7 +73,7 @@ func loopbackUp() error {
 	return netlink.LinkSetUp(iface)
 }
 
-func (n *networkNamespace) AddInterface(i *Interface) error {
+func (n *networkNamespace) AddInterface(i *driverapi.Interface) error {
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
@@ -109,15 +116,33 @@ func (n *networkNamespace) AddInterface(i *Interface) error {
 		return err
 	}
 
-	n.interfaces = append(n.interfaces, i)
+	n.sinfo.Interfaces = append(n.sinfo.Interfaces, i)
 	return nil
 }
 
-func (n *networkNamespace) Interfaces() []*Interface {
-	return n.interfaces
+func (n *networkNamespace) SetGateway(gw string) error {
+	err := setGatewayIP(gw)
+	if err == nil {
+		n.sinfo.Gateway = gw
+	}
+
+	return err
+}
+
+func (n *networkNamespace) SetGatewayIPv6(gw string) error {
+	err := setGatewayIP(gw)
+	if err == nil {
+		n.sinfo.GatewayIPv6 = gw
+	}
+
+	return err
+}
+
+func (n *networkNamespace) Interfaces() []*driverapi.Interface {
+	return n.sinfo.Interfaces
 }
 
-func (n *networkNamespace) Path() string {
+func (n *networkNamespace) Key() string {
 	return n.path
 }
 

+ 25 - 0
libnetwork/sandbox/sandbox.go

@@ -0,0 +1,25 @@
+package sandbox
+
+import "github.com/docker/libnetwork/driverapi"
+
+// Sandbox represents a network sandbox, identified by a specific key.  It
+// holds a list of Interfaces, routes etc, and more can be added dynamically.
+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() []*driverapi.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.
+	AddInterface(*driverapi.Interface) error
+
+	SetGateway(gw string) error
+
+	SetGatewayIPv6(gw string) error
+}

+ 54 - 0
libnetwork/sandbox/sandbox_linux_test.go

@@ -0,0 +1,54 @@
+package sandbox
+
+import (
+	"os"
+	"path/filepath"
+	"runtime"
+	"testing"
+
+	"github.com/docker/libcontainer/utils"
+	"github.com/vishvananda/netns"
+)
+
+func newKey(t *testing.T) (string, error) {
+	name, err := utils.GenerateRandomName("netns", 12)
+	if err != nil {
+		return "", err
+	}
+
+	name = filepath.Join("/tmp", name)
+	if _, err := os.Create(name); err != nil {
+		return "", err
+	}
+
+	return name, nil
+}
+
+func verifySandbox(t *testing.T, s Sandbox) {
+	_, ok := s.(*networkNamespace)
+	if !ok {
+		t.Fatalf("The sandox interface returned is not of type networkNamespace")
+	}
+
+	origns, err := netns.Get()
+	if err != nil {
+		t.Fatalf("Could not get the current netns: %v", err)
+	}
+	defer origns.Close()
+
+	f, err := os.OpenFile(s.Key(), os.O_RDONLY, 0)
+	if err != nil {
+		t.Fatalf("Failed top open network namespace path %q: %v", s.Key(), err)
+	}
+	defer f.Close()
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	nsFD := f.Fd()
+	if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
+		t.Fatalf("Setting to the namespace pointed to by the sandbox %s failed: %v", s.Key(), err)
+	}
+
+	netns.Set(origns)
+}

+ 21 - 0
libnetwork/sandbox/sandbox_test.go

@@ -0,0 +1,21 @@
+package sandbox
+
+import "testing"
+
+func TestSandboxCreate(t *testing.T) {
+	key, err := newKey(t)
+	if err != nil {
+		t.Fatalf("Failed to obtain a key: %v", err)
+	}
+
+	s, err := NewSandbox(key)
+	if err != nil {
+		t.Fatalf("Failed to create a new sandbox: %v", err)
+	}
+
+	if s.Key() != key {
+		t.Fatalf("s.Key() returned %s. Expected %s", s.Key(), key)
+	}
+
+	verifySandbox(t, s)
+}