diff --git a/libnetwork/drivers/bridge/bridge_test.go b/libnetwork/drivers/bridge/bridge_test.go new file mode 100644 index 0000000000..fb71c257b9 --- /dev/null +++ b/libnetwork/drivers/bridge/bridge_test.go @@ -0,0 +1,53 @@ +package bridge + +import ( + "net" + "testing" + + "github.com/docker/libnetwork" +) + +func TestCreate(t *testing.T) { + defer libnetwork.SetupTestNetNS(t)() + + config := &Configuration{BridgeName: DefaultBridgeName} + netw, err := Create(config) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + if expected := NetworkType; netw.Type() != NetworkType { + t.Fatalf("Expected networkType %q, got %q", expected, netw.Type()) + } +} + +func TestCreateFail(t *testing.T) { + defer libnetwork.SetupTestNetNS(t)() + + config := &Configuration{BridgeName: "dummy0"} + if _, err := Create(config); err == nil { + t.Fatal("Bridge creation was expected to fail") + } +} + +func TestCreateFullOptions(t *testing.T) { + defer libnetwork.SetupTestNetNS(t)() + + config := &Configuration{ + BridgeName: DefaultBridgeName, + EnableIPv6: true, + FixedCIDR: bridgeNetworks[0], + EnableIPTables: true, + EnableIPForwarding: true, + } + _, config.FixedCIDRv6, _ = net.ParseCIDR("2001:db8::/48") + + netw, err := Create(config) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + if expected := NetworkType; netw.Type() != NetworkType { + t.Fatalf("Expected networkType %q, got %q", expected, netw.Type()) + } +} diff --git a/libnetwork/drivers/bridge/interface.go b/libnetwork/drivers/bridge/interface.go index 414f42ea9f..87341a927b 100644 --- a/libnetwork/drivers/bridge/interface.go +++ b/libnetwork/drivers/bridge/interface.go @@ -44,5 +44,8 @@ func (i *Interface) Addresses() (netlink.Addr, []netlink.Addr, error) { return netlink.Addr{}, nil, err } + if len(v4addr) == 0 { + return netlink.Addr{}, v6addr, nil + } return v4addr[0], v6addr, nil } diff --git a/libnetwork/drivers/bridge/interface_test.go b/libnetwork/drivers/bridge/interface_test.go new file mode 100644 index 0000000000..1f9addda30 --- /dev/null +++ b/libnetwork/drivers/bridge/interface_test.go @@ -0,0 +1,32 @@ +package bridge + +import ( + "testing" + + "github.com/docker/libnetwork" + "github.com/vishvananda/netlink" +) + +func TestInterfaceDefaultName(t *testing.T) { + defer libnetwork.SetupTestNetNS(t)() + + if inf := NewInterface(&Configuration{}); inf.Config.BridgeName != DefaultBridgeName { + t.Fatalf("Expected default interface name %q, got %q", DefaultBridgeName, inf.Config.BridgeName) + } +} + +func TestAddressesEmptyInterface(t *testing.T) { + defer libnetwork.SetupTestNetNS(t)() + + inf := NewInterface(&Configuration{}) + addrv4, addrsv6, err := inf.Addresses() + if err != nil { + t.Fatalf("Failed to get addresses of default interface: %v", err) + } + if expected := (netlink.Addr{}); addrv4 != expected { + t.Fatalf("Default interface has unexpected IPv4: %s", addrv4) + } + if len(addrsv6) != 0 { + t.Fatalf("Default interface has unexpected IPv6: %v", addrsv6) + } +} diff --git a/libnetwork/drivers/bridge/resolvconf_test.go b/libnetwork/drivers/bridge/resolvconf_test.go new file mode 100644 index 0000000000..029f41c78c --- /dev/null +++ b/libnetwork/drivers/bridge/resolvconf_test.go @@ -0,0 +1,53 @@ +package bridge + +import ( + "bytes" + "testing" +) + +func TestResolveConfRead(t *testing.T) { + b, err := readResolvConf() + if err != nil { + t.Fatalf("Failed to read resolv.conf: %v", err) + } + + if b == nil { + t.Fatal("Reading resolv.conf returned no content") + } +} + +func TestResolveConfReadLines(t *testing.T) { + commentChar := []byte("#") + + b, _ := readResolvConf() + lines := getLines(b, commentChar) + if lines == nil { + t.Fatal("Failed to read resolv.conf lines") + } + + for _, line := range lines { + if bytes.Index(line, commentChar) != -1 { + t.Fatal("Returned comment content from resolv.conf") + } + } +} + +func TestResolvConfNameserversAsCIDR(t *testing.T) { + resolvConf := `# Commented line +nameserver 1.2.3.4 + +nameserver 5.6.7.8 # Test +` + + cidrs := getNameserversAsCIDR([]byte(resolvConf)) + if expected := 2; len(cidrs) != expected { + t.Fatalf("Expected %d nameservers, got %d", expected, len(cidrs)) + } + + expected := []string{"1.2.3.4/32", "5.6.7.8/32"} + for i, exp := range expected { + if cidrs[i] != exp { + t.Fatalf("Expected nameservers %s, got %s", exp, cidrs[i]) + } + } +} diff --git a/libnetwork/drivers/bridge/setup_fixedcidrv4.go b/libnetwork/drivers/bridge/setup_fixedcidrv4.go index bf5dc2701a..464bddfef4 100644 --- a/libnetwork/drivers/bridge/setup_fixedcidrv4.go +++ b/libnetwork/drivers/bridge/setup_fixedcidrv4.go @@ -1,6 +1,8 @@ package bridge import ( + "fmt" + log "github.com/Sirupsen/logrus" "github.com/docker/docker/daemon/networkdriver/ipallocator" ) @@ -12,5 +14,9 @@ func SetupFixedCIDRv4(i *Interface) error { } log.Debugf("Using IPv4 subnet: %v", i.Config.FixedCIDR) - return ipallocator.RegisterSubnet(addrv4.IPNet, i.Config.FixedCIDR) + if err := ipallocator.RegisterSubnet(addrv4.IPNet, i.Config.FixedCIDR); err != nil { + return fmt.Errorf("Setup FixedCIDRv4 failed for subnet %s in %s: %v", i.Config.FixedCIDR, addrv4.IPNet, err) + } + + return nil } diff --git a/libnetwork/drivers/bridge/setup_fixedcidrv4_test.go b/libnetwork/drivers/bridge/setup_fixedcidrv4_test.go new file mode 100644 index 0000000000..422b13c7d0 --- /dev/null +++ b/libnetwork/drivers/bridge/setup_fixedcidrv4_test.go @@ -0,0 +1,59 @@ +package bridge + +import ( + "net" + "testing" + + "github.com/docker/docker/daemon/networkdriver/ipallocator" + "github.com/docker/libnetwork" +) + +func TestSetupFixedCIDRv4(t *testing.T) { + defer libnetwork.SetupTestNetNS(t)() + + br := &Interface{ + Config: &Configuration{ + BridgeName: DefaultBridgeName, + AddressIPv4: &net.IPNet{IP: net.ParseIP("192.168.1.1"), Mask: net.CIDRMask(16, 32)}, + FixedCIDR: &net.IPNet{IP: net.ParseIP("192.168.2.0"), Mask: net.CIDRMask(24, 32)}, + }, + } + if err := SetupDevice(br); err != nil { + t.Fatalf("Bridge creation failed: %v", err) + } + if err := SetupBridgeIPv4(br); err != nil { + t.Fatalf("Assign IPv4 to bridge failed: %v", err) + } + + if err := SetupFixedCIDRv4(br); err != nil { + t.Fatalf("Failed to setup bridge FixedCIDRv4: %v", err) + } + + if ip, err := ipallocator.RequestIP(br.Config.FixedCIDR, nil); err != nil { + t.Fatalf("Failed to request IP to allocator: %v", err) + } else if expected := "192.168.2.1"; ip.String() != expected { + t.Fatalf("Expected allocated IP %s, got %s", expected, ip) + } +} + +func TestSetupBadFixedCIDRv4(t *testing.T) { + defer libnetwork.SetupTestNetNS(t)() + + br := &Interface{ + Config: &Configuration{ + BridgeName: DefaultBridgeName, + AddressIPv4: &net.IPNet{IP: net.ParseIP("192.168.1.1"), Mask: net.CIDRMask(24, 32)}, + FixedCIDR: &net.IPNet{IP: net.ParseIP("192.168.2.0"), Mask: net.CIDRMask(24, 32)}, + }, + } + if err := SetupDevice(br); err != nil { + t.Fatalf("Bridge creation failed: %v", err) + } + if err := SetupBridgeIPv4(br); err != nil { + t.Fatalf("Assign IPv4 to bridge failed: %v", err) + } + + if err := SetupFixedCIDRv4(br); err == nil { + t.Fatal("Setup bridge FixedCIDRv4 should have failed") + } +} diff --git a/libnetwork/drivers/bridge/setup_ipv4.go b/libnetwork/drivers/bridge/setup_ipv4.go index cfbc6b0d1c..c37d341bb2 100644 --- a/libnetwork/drivers/bridge/setup_ipv4.go +++ b/libnetwork/drivers/bridge/setup_ipv4.go @@ -105,13 +105,11 @@ func checkRouteOverlaps(toCheck *net.IPNet) error { } func networkOverlaps(netX *net.IPNet, netY *net.IPNet) bool { - if len(netX.IP) == len(netY.IP) { - if firstIP, _ := networkRange(netX); netY.Contains(firstIP) { - return true - } - if firstIP, _ := networkRange(netY); netX.Contains(firstIP) { - return true - } + if firstIP, _ := networkRange(netX); netY.Contains(firstIP) { + return true + } + if firstIP, _ := networkRange(netY); netX.Contains(firstIP) { + return true } return false } diff --git a/libnetwork/drivers/bridge/setup_verify.go b/libnetwork/drivers/bridge/setup_verify.go index 7baf8cf3e2..cd77fd521d 100644 --- a/libnetwork/drivers/bridge/setup_verify.go +++ b/libnetwork/drivers/bridge/setup_verify.go @@ -1,6 +1,10 @@ package bridge -import "fmt" +import ( + "fmt" + + "github.com/vishvananda/netlink" +) func SetupVerifyConfiguredAddresses(i *Interface) error { // Fetch a single IPv4 and a slice of IPv6 addresses from the bridge. @@ -9,6 +13,11 @@ func SetupVerifyConfiguredAddresses(i *Interface) error { return err } + // Verify that the bridge does have an IPv4 address. + if addrv4.IPNet == nil { + return fmt.Errorf("Bridge has no IPv4 address configured") + } + // Verify that the bridge IPv4 address matches the requested configuration. if i.Config.AddressIPv4 != nil && !addrv4.IP.Equal(i.Config.AddressIPv4.IP) { return fmt.Errorf("Bridge IPv4 (%s) does not match requested configuration %s", addrv4.IP, i.Config.AddressIPv4.IP) @@ -16,11 +25,18 @@ func SetupVerifyConfiguredAddresses(i *Interface) error { // Verify that one of the bridge IPv6 addresses matches the requested // configuration. - for _, addrv6 := range addrsv6 { - if addrv6.String() == BridgeIPv6.String() { - return nil - } + if i.Config.EnableIPv6 && !findIPv6Address(netlink.Addr{IPNet: BridgeIPv6}, addrsv6) { + return fmt.Errorf("Bridge IPv6 addresses do not match the expected bridge configuration %s", BridgeIPv6) } - return fmt.Errorf("Bridge IPv6 addresses do not match the expected bridge configuration %s", BridgeIPv6) + return nil +} + +func findIPv6Address(addr netlink.Addr, addresses []netlink.Addr) bool { + for _, addrv6 := range addresses { + if addrv6.String() == addr.String() { + return true + } + } + return false } diff --git a/libnetwork/drivers/bridge/setup_verify_test.go b/libnetwork/drivers/bridge/setup_verify_test.go new file mode 100644 index 0000000000..dd4ae5ba19 --- /dev/null +++ b/libnetwork/drivers/bridge/setup_verify_test.go @@ -0,0 +1,105 @@ +package bridge + +import ( + "net" + "testing" + + "github.com/docker/libnetwork" + "github.com/vishvananda/netlink" +) + +func setupVerifyTest(t *testing.T) *Interface { + inf := &Interface{Config: &Configuration{}} + + br := netlink.Bridge{} + br.LinkAttrs.Name = "default0" + if err := netlink.LinkAdd(&br); err == nil { + inf.Link = &br + } else { + t.Fatalf("Failed to create bridge interface: %v", err) + } + + return inf +} + +func TestSetupVerify(t *testing.T) { + defer libnetwork.SetupTestNetNS(t)() + + addrv4 := net.IPv4(192, 168, 1, 1) + inf := setupVerifyTest(t) + inf.Config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()} + + if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: inf.Config.AddressIPv4}); err != nil { + t.Fatalf("Failed to assign IPv4 %s to interface: %v", inf.Config.AddressIPv4, err) + } + + if err := SetupVerifyConfiguredAddresses(inf); err != nil { + t.Fatalf("Address verification failed: %v", err) + } +} + +func TestSetupVerifyBad(t *testing.T) { + defer libnetwork.SetupTestNetNS(t)() + + addrv4 := net.IPv4(192, 168, 1, 1) + inf := setupVerifyTest(t) + inf.Config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()} + + ipnet := &net.IPNet{IP: net.IPv4(192, 168, 1, 2), Mask: addrv4.DefaultMask()} + if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: ipnet}); err != nil { + t.Fatalf("Failed to assign IPv4 %s to interface: %v", ipnet, err) + } + + if err := SetupVerifyConfiguredAddresses(inf); err == nil { + t.Fatal("Address verification was expected to fail") + } +} + +func TestSetupVerifyMissing(t *testing.T) { + defer libnetwork.SetupTestNetNS(t)() + + addrv4 := net.IPv4(192, 168, 1, 1) + inf := setupVerifyTest(t) + inf.Config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()} + + if err := SetupVerifyConfiguredAddresses(inf); err == nil { + t.Fatal("Address verification was expected to fail") + } +} + +func TestSetupVerifyIPv6(t *testing.T) { + defer libnetwork.SetupTestNetNS(t)() + + addrv4 := net.IPv4(192, 168, 1, 1) + inf := setupVerifyTest(t) + inf.Config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()} + inf.Config.EnableIPv6 = true + + if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: BridgeIPv6}); err != nil { + t.Fatalf("Failed to assign IPv6 %s to interface: %v", BridgeIPv6, err) + } + if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: inf.Config.AddressIPv4}); err != nil { + t.Fatalf("Failed to assign IPv4 %s to interface: %v", inf.Config.AddressIPv4, err) + } + + if err := SetupVerifyConfiguredAddresses(inf); err != nil { + t.Fatalf("Address verification failed: %v", err) + } +} + +func TestSetupVerifyIPv6Missing(t *testing.T) { + defer libnetwork.SetupTestNetNS(t)() + + addrv4 := net.IPv4(192, 168, 1, 1) + inf := setupVerifyTest(t) + inf.Config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()} + inf.Config.EnableIPv6 = true + + if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: inf.Config.AddressIPv4}); err != nil { + t.Fatalf("Failed to assign IPv4 %s to interface: %v", inf.Config.AddressIPv4, err) + } + + if err := SetupVerifyConfiguredAddresses(inf); err == nil { + t.Fatal("Address verification was expected to fail") + } +} diff --git a/libnetwork/network.go b/libnetwork/network.go index dd2cce297b..60e9cd0b0c 100644 --- a/libnetwork/network.go +++ b/libnetwork/network.go @@ -17,7 +17,7 @@ // // 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.CreateInterfaces(containerID) +// interfaces, err := network.Link(containerID) // if err != nil { // return err // }