From 69437b10093d12266bb66b12f2c9f36339dfa90b Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Thu, 23 Apr 2015 11:15:15 -0700 Subject: [PATCH] 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 --- libnetwork/cmd/readme_test/readme.go | 2 +- libnetwork/drivers/bridge/bridge.go | 51 ++++++++++++++++++++--- libnetwork/drivers/bridge/bridge_test.go | 35 ++++++++++++++++ libnetwork/drivers/bridge/error.go | 3 ++ libnetwork/drivers/bridge/network_test.go | 18 ++++---- libnetwork/libnetwork_test.go | 6 +-- libnetwork/network.go | 2 +- 7 files changed, 97 insertions(+), 20 deletions(-) diff --git a/libnetwork/cmd/readme_test/readme.go b/libnetwork/cmd/readme_test/readme.go index ea55f274eb..9abce64cfd 100644 --- a/libnetwork/cmd/readme_test/readme.go +++ b/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 } diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index 028ef374e7..6134f3da4d 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/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) diff --git a/libnetwork/drivers/bridge/bridge_test.go b/libnetwork/drivers/bridge/bridge_test.go index a085209915..8eac37a0ec 100644 --- a/libnetwork/drivers/bridge/bridge_test.go +++ b/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") + } +} diff --git a/libnetwork/drivers/bridge/error.go b/libnetwork/drivers/bridge/error.go index dd8907d318..e26e5f113c 100644 --- a/libnetwork/drivers/bridge/error.go +++ b/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") diff --git a/libnetwork/drivers/bridge/network_test.go b/libnetwork/drivers/bridge/network_test.go index 109e8ca6a3..9680ec0ee8 100644 --- a/libnetwork/drivers/bridge/network_test.go +++ b/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()) } diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index 62278fc5b0..f4f4b74d0e 100644 --- a/libnetwork/libnetwork_test.go +++ b/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) } diff --git a/libnetwork/network.go b/libnetwork/network.go index 9fccfc3657..bcd9eccbe1 100644 --- a/libnetwork/network.go +++ b/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 }