Browse Source

Libnetwork bridge to handle --mac-address option

- This addresses one requirement from Issue #79
- Defined EndpointConfiguration struct for bridge driver
  which contains the user's preferred mac address for the
  sanbox interface

Signed-off-by: Alessandro Boch <aboch@docker.com>
Alessandro Boch 10 years ago
parent
commit
69437b1009

+ 1 - 1
libnetwork/cmd/readme_test/readme.go

@@ -35,7 +35,7 @@ func main() {
 	// settings will be used for container infos (inspect and such), as well as
 	// iptables rules for port publishing. This info is contained or accessible
 	// from the returned endpoint.
-	ep, err := network.CreateEndpoint("Endpoint1", networkNamespace.Key(), "")
+	ep, err := network.CreateEndpoint("Endpoint1", networkNamespace.Key(), nil)
 	if err != nil {
 		return
 	}

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

@@ -41,9 +41,15 @@ type Configuration struct {
 	AllowNonDefaultBridge bool
 }
 
+// EndpointConfiguration represents the user specified configuration for the sandbox endpoint
+type EndpointConfiguration struct {
+	MacAddress net.HardwareAddr
+}
+
 type bridgeEndpoint struct {
-	id   types.UUID
-	port *sandbox.Interface
+	id     types.UUID
+	port   *sandbox.Interface
+	config *EndpointConfiguration // User specified parameters
 }
 
 type bridgeNetwork struct {
@@ -239,7 +245,7 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
 	return err
 }
 
-func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOption interface{}) (*sandbox.Info, error) {
+func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions interface{}) (*sandbox.Info, error) {
 	var (
 		ipv6Addr *net.IPNet
 		err      error
@@ -285,8 +291,14 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOption in
 		return nil, driverapi.ErrEndpointExists
 	}
 
+	// Try to convert the options to endpoint configuration
+	epConfig, err := parseEndpointOptions(epOptions)
+	if err != nil {
+		return nil, err
+	}
+
 	// Create and add the endpoint
-	endpoint := &bridgeEndpoint{id: eid}
+	endpoint := &bridgeEndpoint{id: eid, config: epConfig}
 	n.endpoints[sboxKey] = endpoint
 	n.Unlock()
 
@@ -335,6 +347,15 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOption in
 	if err != nil {
 		return nil, err
 	}
+
+	// Add user specified attributes
+	if epConfig != nil && epConfig.MacAddress != nil {
+		err = netlink.LinkSetHardwareAddr(sbox, epConfig.MacAddress)
+		if err != nil {
+			return nil, err
+		}
+	}
+
 	defer func() {
 		if err != nil {
 			netlink.LinkDel(sbox)
@@ -347,14 +368,14 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOption in
 		return nil, err
 	}
 
-	// Reuqest a v4 address for the sandbox side pipe interface
+	// v4 address for the sandbox side pipe interface
 	ip4, err := ipAllocator.RequestIP(n.bridge.bridgeIPv4, nil)
 	if err != nil {
 		return nil, err
 	}
 	ipv4Addr := &net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask}
 
-	// Request a v6 address for the sandbox side pipe interface
+	// v6 address for the sandbox side pipe interface
 	if config.EnableIPv6 {
 		ip6, err := ipAllocator.RequestIP(n.bridge.bridgeIPv6, nil)
 		if err != nil {
@@ -454,6 +475,24 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
 	return nil
 }
 
+func parseEndpointOptions(epOptions interface{}) (*EndpointConfiguration, error) {
+	if epOptions == nil {
+		return nil, nil
+	}
+	switch opt := epOptions.(type) {
+	case options.Generic:
+		opaqueConfig, err := options.GenerateFromModel(opt, &EndpointConfiguration{})
+		if err != nil {
+			return nil, err
+		}
+		return opaqueConfig.(*EndpointConfiguration), nil
+	case *EndpointConfiguration:
+		return opt, nil
+	default:
+		return nil, ErrInvalidEndpointConfig
+	}
+}
+
 // Generates a name to be used for a virtual ethernet
 // interface. The name is constructed by 'veth' appended
 // by a randomly generated hex value. (example: veth0f60e2c)

+ 35 - 0
libnetwork/drivers/bridge/bridge_test.go

@@ -1,10 +1,12 @@
 package bridge
 
 import (
+	"bytes"
 	"net"
 	"testing"
 
 	"github.com/docker/libnetwork/netutils"
+	"github.com/vishvananda/netlink"
 )
 
 func TestCreate(t *testing.T) {
@@ -56,3 +58,36 @@ func TestCreateFullOptions(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 }
+func TestCreateLinkWithOptions(t *testing.T) {
+	defer netutils.SetupTestNetNS(t)()
+
+	_, d := New()
+
+	config := &Configuration{BridgeName: DefaultBridgeName}
+	if err := d.Config(config); err != nil {
+		t.Fatalf("Failed to setup driver config: %v", err)
+	}
+
+	err := d.CreateNetwork("net1", "")
+	if err != nil {
+		t.Fatalf("Failed to create bridge: %v", err)
+	}
+
+	mac := net.HardwareAddr([]byte{0x1e, 0x67, 0x66, 0x44, 0x55, 0x66})
+	epConf := &EndpointConfiguration{MacAddress: mac}
+
+	sinfo, err := d.CreateEndpoint("net1", "ep", "s1", epConf)
+	if err != nil {
+		t.Fatalf("Failed to create a link: %s", err.Error())
+	}
+
+	ifaceName := sinfo.Interfaces[0].SrcName
+	veth, err := netlink.LinkByName(ifaceName)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !bytes.Equal(mac, veth.Attrs().HardwareAddr) {
+		t.Fatalf("Failed to parse and program endpoint configuration")
+	}
+}

+ 3 - 0
libnetwork/drivers/bridge/error.go

@@ -13,6 +13,9 @@ var (
 	// ErrInvalidConfig error is returned when a network is created on a driver without valid config.
 	ErrInvalidConfig = errors.New("trying to create a network on a driver without valid config")
 
+	// ErrInvalidEndpointConfig error is returned when a endpoint create is attempted with an invalid endpoint configuration.
+	ErrInvalidEndpointConfig = errors.New("trying to create an endpoint with an invalid endpoint configuration")
+
 	// ErrNetworkExists error is returned when a network already exists and another network is created.
 	ErrNetworkExists = errors.New("network already exists, simplebridge can only have one network")
 

+ 9 - 9
libnetwork/drivers/bridge/network_test.go

@@ -25,7 +25,7 @@ func TestLinkCreate(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 
-	sinfo, err := d.CreateEndpoint("dummy", "", "sb1", "")
+	sinfo, err := d.CreateEndpoint("dummy", "", "sb1", nil)
 	if err != nil {
 		if _, ok := err.(InvalidEndpointIDError); !ok {
 			t.Fatalf("Failed with a wrong error :%s", err.Error())
@@ -34,7 +34,7 @@ func TestLinkCreate(t *testing.T) {
 		t.Fatalf("Failed to detect invalid config")
 	}
 
-	sinfo, err = d.CreateEndpoint("dummy", "ep", "", "")
+	sinfo, err = d.CreateEndpoint("dummy", "ep", "", nil)
 	if err != nil {
 		if _, ok := err.(InvalidSandboxIDError); !ok {
 			t.Fatalf("Failed with a wrong error :%s", err.Error())
@@ -43,17 +43,17 @@ func TestLinkCreate(t *testing.T) {
 		t.Fatalf("Failed to detect invalid config")
 	}
 
-	sinfo, err = d.CreateEndpoint("dummy", "ep", "cc", "")
+	sinfo, err = d.CreateEndpoint("dummy", "ep", "cc", nil)
 	if err != nil {
 		t.Fatalf("Failed to create a link: %s", err.Error())
 	}
 
-	_, err = d.CreateEndpoint("dummy", "ep", "cc2", "")
+	_, err = d.CreateEndpoint("dummy", "ep", "cc2", nil)
 	if err == nil {
 		t.Fatalf("Failed to detect duplicate endpoint id on same network")
 	}
 
-	_, err = d.CreateEndpoint("dummy", "ep2", "cc", "")
+	_, err = d.CreateEndpoint("dummy", "ep2", "cc", nil)
 	if err == nil {
 		t.Fatalf("Failed to detect addition of more than one endpoint to same sandbox")
 	}
@@ -110,12 +110,12 @@ func TestLinkCreateTwo(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 
-	_, err = d.CreateEndpoint("dummy", "ep", "s1", "")
+	_, err = d.CreateEndpoint("dummy", "ep", "s1", nil)
 	if err != nil {
 		t.Fatalf("Failed to create a link: %s", err.Error())
 	}
 
-	_, err = d.CreateEndpoint("dummy", "ep", "s1", "")
+	_, err = d.CreateEndpoint("dummy", "ep", "s1", nil)
 	if err != nil {
 		if err != driverapi.ErrEndpointExists {
 			t.Fatalf("Failed with a wrong error :%s", err.Error())
@@ -140,7 +140,7 @@ func TestLinkCreateNoEnableIPv6(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 
-	sinfo, err := d.CreateEndpoint("dummy", "ep", "sb2", "")
+	sinfo, err := d.CreateEndpoint("dummy", "ep", "sb2", nil)
 	if err != nil {
 		t.Fatalf("Failed to create a link: %s", err.Error())
 	}
@@ -171,7 +171,7 @@ func TestLinkDelete(t *testing.T) {
 		t.Fatalf("Failed to create bridge: %v", err)
 	}
 
-	_, err = d.CreateEndpoint("dummy", "ep1", "s1", "")
+	_, err = d.CreateEndpoint("dummy", "ep1", "s1", nil)
 	if err != nil {
 		t.Fatalf("Failed to create a link: %s", err.Error())
 	}

+ 3 - 3
libnetwork/libnetwork_test.go

@@ -67,7 +67,7 @@ func TestSimplebridge(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	ep, err := network.CreateEndpoint("testep", "sb1", "")
+	ep, err := network.CreateEndpoint("testep", "sb1", nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -205,7 +205,7 @@ func TestDeleteNetworkWithActiveEndpoints(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	ep, err := network.CreateEndpoint("testep", "sb2", "")
+	ep, err := network.CreateEndpoint("testep", "sb2", nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -273,7 +273,7 @@ func TestUnknownEndpoint(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	ep, err := network.CreateEndpoint("testep", "sb1", "")
+	ep, err := network.CreateEndpoint("testep", "sb1", nil)
 	if err != nil {
 		t.Fatal(err)
 	}

+ 1 - 1
libnetwork/network.go

@@ -31,7 +31,7 @@ create network namespaces and allocate interfaces for containers to use.
     // 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.
-    ep, err := network.CreateEndpoint("Endpoint1", networkNamespace.Key(), "")
+    ep, err := network.CreateEndpoint("Endpoint1", networkNamespace.Key(), nil)
     if err != nil {
 	    return
     }