Libnetwork refactor for container network model

- Added controller, network, endpoint and sandbox interfaces
    - Created netutils package for miscallaneous network utilities
    - Created driverapi package to break cyclic dependency b/w driver and libnetwork
    - Made libnetwork multithread safe
    - Made bridge driver multithread safe
    - Fixed README.md

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
This commit is contained in:
Jana Radhakrishnan 2015-04-13 18:40:42 +00:00
parent 53541c24a4
commit 68ae284db5
31 changed files with 910 additions and 483 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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
type driverTable map[string]driverapi.Driver
// 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)
func enumerateDrivers() driverTable {
var drivers driverTable
for _, fn := range [](func() (string, driverapi.Driver)){bridge.New} {
name, driver := fn()
drivers[name] = driver
}
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)
}
config, err := options.GenerateFromModel(options.Generic(generic), d.creatorArg)
if err != nil {
return nil, fmt.Errorf("failed to generate driver config: %v", err)
}
return d.creatorFn.CreateNetwork(name, config)
return drivers
}

View File

@ -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
}
return &bridgeNetwork{NetworkName: name, bridge: bridgeIntfc}, nil
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()
}
}()
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")
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 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
}
// 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
}
*/
package libnetwork
// 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
import (
"fmt"
"sync"
// The name that will be assigned to the interface once moves inside a
// network namespace.
DstName string
"github.com/docker/docker/pkg/common"
"github.com/docker/libnetwork/driverapi"
)
// IPv4 address for the interface.
Address string
// IPv6 address for the interface.
AddressIPv6 string
// IPv4 gateway for the interface.
Gateway string
// 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
}
// 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 endpoint struct {
name string
id driverapi.UUID
network *network
sandboxInfo *driverapi.SandboxInfo
}
// 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 network struct {
ctrlr *controller
name string
networkType string
id driverapi.UUID
endpoints map[driverapi.UUID]*endpoint
sync.Mutex
}
type networkTable map[driverapi.UUID]*network
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
}

View File

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

View File

@ -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) Path() string {
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) Key() string {
return n.path
}

View File

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

View File

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

View File

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