diff --git a/libnetwork/configure.go b/libnetwork/configure.go index 31a6571d86..35cfab743d 100644 --- a/libnetwork/configure.go +++ b/libnetwork/configure.go @@ -14,10 +14,8 @@ func configureInterface(iface netlink.Link, settings *Interface) error { ErrMessage string }{ {setInterfaceName, fmt.Sprintf("error renaming interface %q to %q", ifaceName, settings.DstName)}, - {setInterfaceMAC, fmt.Sprintf("error setting interface %q MAC address to %q", ifaceName, settings.MacAddress)}, {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)}, - {setInterfaceMTU, fmt.Sprintf("error setting interface %q MTU to %q", ifaceName, settings.MTU)}, {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)}, } @@ -78,18 +76,6 @@ func setInterfaceIPv6(iface netlink.Link, settings *Interface) (err error) { return err } -func setInterfaceMAC(iface netlink.Link, settings *Interface) (err error) { - var hwAddr net.HardwareAddr - if hwAddr, err = net.ParseMAC(settings.MacAddress); err == nil { - err = netlink.LinkSetHardwareAddr(iface, hwAddr) - } - return err -} - -func setInterfaceMTU(iface netlink.Link, settings *Interface) error { - return netlink.LinkSetMTU(iface, settings.MTU) -} - func setInterfaceName(iface netlink.Link, settings *Interface) error { return netlink.LinkSetName(iface, settings.DstName) } diff --git a/libnetwork/drivers/bridge/interface.go b/libnetwork/drivers/bridge/interface.go index fa157304b8..372ebd84a5 100644 --- a/libnetwork/drivers/bridge/interface.go +++ b/libnetwork/drivers/bridge/interface.go @@ -1,6 +1,10 @@ package bridge -import "github.com/vishvananda/netlink" +import ( + "net" + + "github.com/vishvananda/netlink" +) const ( // DefaultBridgeName is the default name for the bridge interface managed @@ -10,8 +14,10 @@ const ( // Interface models the bridge network device. type bridgeInterface struct { - Config *Configuration - Link netlink.Link + Config *Configuration + Link netlink.Link + bridgeIPv4 *net.IPNet + bridgeIPv6 *net.IPNet } // NewInterface creates a new bridge interface structure. It attempts to find diff --git a/libnetwork/drivers/bridge/network.go b/libnetwork/drivers/bridge/network.go index d9a8858645..a4df0db25b 100644 --- a/libnetwork/drivers/bridge/network.go +++ b/libnetwork/drivers/bridge/network.go @@ -1,13 +1,23 @@ package bridge import ( + "errors" + "net" + "strings" + + "github.com/docker/libcontainer/utils" "github.com/docker/libnetwork" + "github.com/docker/libnetwork/ipallocator" "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 { @@ -19,8 +29,99 @@ func (b *bridgeNetwork) Type() string { } func (b *bridgeNetwork) Link(name string) ([]*libnetwork.Interface, error) { - // TODO - return nil, nil + + 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 { diff --git a/libnetwork/drivers/bridge/network_test.go b/libnetwork/drivers/bridge/network_test.go new file mode 100644 index 0000000000..2815ca1cfa --- /dev/null +++ b/libnetwork/drivers/bridge/network_test.go @@ -0,0 +1,119 @@ +package bridge + +import ( + "net" + "testing" + + "github.com/docker/libnetwork" + "github.com/vishvananda/netlink" +) + +func TestLinkCreate(t *testing.T) { + defer libnetwork.SetupTestNetNS(t)() + d := &driver{} + + config := &Configuration{ + BridgeName: DefaultBridgeName, + EnableIPv6: true} + netw, err := d.CreateNetwork("dummy", config) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + interfaces, err := netw.Link("ep") + if err != nil { + t.Fatalf("Failed to create a link: %v", err) + } + + if len(interfaces) != 1 { + t.Fatalf("Expected exactly one interface. Instead got %d interface(s)", len(interfaces)) + } + + if interfaces[0].DstName == "" { + t.Fatal("Invalid Dstname returned") + } + + _, err = netlink.LinkByName(interfaces[0].SrcName) + if err != nil { + t.Fatalf("Could not find source link %s: %v", interfaces[0].SrcName, err) + } + + ip, _, err := net.ParseCIDR(interfaces[0].Address) + if err != nil { + 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()) + } + + ip6, _, err := net.ParseCIDR(interfaces[0].AddressIPv6) + if err != nil { + t.Fatalf("Invalid IPv6 address returned, ip = %s: %v", interfaces[0].AddressIPv6, err) + } + + if !b.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 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) + } +} + +func TestLinkCreateTwo(t *testing.T) { + defer libnetwork.SetupTestNetNS(t)() + d := &driver{} + + config := &Configuration{ + BridgeName: DefaultBridgeName, + EnableIPv6: true} + netw, err := d.CreateNetwork("dummy", config) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + _, err = netw.Link("ep") + if err != nil { + t.Fatalf("Failed to create a link: %v", err) + } + + _, err = netw.Link("ep1") + if err != nil { + if err != ErrEndpointExists { + t.Fatalf("Failed with a wrong error :%v", err) + } + } else { + t.Fatalf("Expected to fail while trying to add more than one endpoint") + } +} + +func TestLinkCreateNoEnableIPv6(t *testing.T) { + defer libnetwork.SetupTestNetNS(t)() + d := &driver{} + + config := &Configuration{ + BridgeName: DefaultBridgeName} + netw, err := d.CreateNetwork("dummy", config) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + interfaces, err := netw.Link("ep") + if err != nil { + t.Fatalf("Failed to create a link: %v", err) + } + + if interfaces[0].AddressIPv6 != "" || + interfaces[0].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) + } +} diff --git a/libnetwork/drivers/bridge/setup_ipv4.go b/libnetwork/drivers/bridge/setup_ipv4.go index bfeb809b22..5f942c4175 100644 --- a/libnetwork/drivers/bridge/setup_ipv4.go +++ b/libnetwork/drivers/bridge/setup_ipv4.go @@ -52,6 +52,8 @@ func setupBridgeIPv4(i *bridgeInterface) error { return fmt.Errorf("Failed to add IPv4 address %s to bridge: %v", bridgeIPv4, err) } + i.bridgeIPv4 = bridgeIPv4 + return nil } diff --git a/libnetwork/drivers/bridge/setup_ipv6.go b/libnetwork/drivers/bridge/setup_ipv6.go index f9dcfc9206..bb61387698 100644 --- a/libnetwork/drivers/bridge/setup_ipv6.go +++ b/libnetwork/drivers/bridge/setup_ipv6.go @@ -33,5 +33,7 @@ func setupBridgeIPv6(i *bridgeInterface) error { return fmt.Errorf("Failed to add IPv6 address %s to bridge: %v", bridgeIPv6, err) } + i.bridgeIPv6 = bridgeIPv6 + return nil } diff --git a/libnetwork/network.go b/libnetwork/network.go index ccb0a10546..9d81745f27 100644 --- a/libnetwork/network.go +++ b/libnetwork/network.go @@ -44,9 +44,6 @@ type Interface struct { // network namespace. DstName string - // MAC address for the interface. - MacAddress string - // IPv4 address for the interface. Address string @@ -58,9 +55,6 @@ type Interface struct { // IPv6 gateway for the interface. GatewayIPv6 string - - // Network MTU. - MTU int } // A Network represents a logical connectivity zone that containers may