diff --git a/libnetwork/endpoint.go b/libnetwork/endpoint.go index 4fbe81b409..9f378b02fc 100644 --- a/libnetwork/endpoint.go +++ b/libnetwork/endpoint.go @@ -495,10 +495,6 @@ func (ep *endpoint) sbJoin(sb *sandbox, options ...EndpointOption) error { return err } - if err = n.getController().updateToStore(ep); err != nil { - return err - } - // Current endpoint providing external connectivity for the sandbox extEp := sb.getGatewayEndpoint() @@ -515,6 +511,10 @@ func (ep *endpoint) sbJoin(sb *sandbox, options ...EndpointOption) error { return err } + if err = n.getController().updateToStore(ep); err != nil { + return err + } + if sb.needDefaultGW() && sb.getEndpointInGWNetwork() == nil { return sb.setupDefaultGW() } diff --git a/libnetwork/libnetwork_linux_test.go b/libnetwork/libnetwork_linux_test.go index 37121bd8b0..7cca41037f 100644 --- a/libnetwork/libnetwork_linux_test.go +++ b/libnetwork/libnetwork_linux_test.go @@ -402,6 +402,20 @@ func externalKeyTest(t *testing.T, reexec bool) { } }() + n2, err := createTestNetwork(bridgeNetType, "testnetwork2", options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork2", + }, + }, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n2.Delete(); err != nil { + t.Fatal(err) + } + }() + ep, err := n.CreateEndpoint("ep1") if err != nil { t.Fatal(err) @@ -413,7 +427,7 @@ func externalKeyTest(t *testing.T, reexec bool) { } }() - ep2, err := n.CreateEndpoint("ep2") + ep2, err := n2.CreateEndpoint("ep2") if err != nil { t.Fatal(err) } diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index 4510174d8b..f68dcfda46 100644 --- a/libnetwork/libnetwork_test.go +++ b/libnetwork/libnetwork_test.go @@ -882,6 +882,20 @@ func TestEndpointDeleteWithActiveContainer(t *testing.T) { } }() + n2, err := createTestNetwork(bridgeNetType, "testnetwork2", options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork2", + }, + }, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n2.Delete(); err != nil { + t.Fatal(err) + } + }() + ep, err := n.CreateEndpoint("ep1") if err != nil { t.Fatal(err) @@ -1012,12 +1026,26 @@ func TestLeaveAll(t *testing.T) { } }() + n2, err := createTestNetwork(bridgeNetType, "testnetwork2", options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork2", + }, + }, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n2.Delete(); err != nil { + t.Fatal(err) + } + }() + ep1, err := n.CreateEndpoint("ep1") if err != nil { t.Fatal(err) } - ep2, err := n.CreateEndpoint("ep2") + ep2, err := n2.CreateEndpoint("ep2") if err != nil { t.Fatal(err) } diff --git a/libnetwork/osl/interface_linux.go b/libnetwork/osl/interface_linux.go index 643939322e..04b85369f7 100644 --- a/libnetwork/osl/interface_linux.go +++ b/libnetwork/osl/interface_linux.go @@ -377,7 +377,9 @@ func setInterfaceIP(nlh *netlink.Handle, iface netlink.Link, i *nwIface) error { if i.Address() == nil { return nil } - + if err := checkRouteConflict(nlh, i.Address(), netlink.FAMILY_V4); err != nil { + return err + } ipAddr := &netlink.Addr{IPNet: i.Address(), Label: ""} return nlh.AddrAdd(iface, ipAddr) } @@ -386,6 +388,9 @@ func setInterfaceIPv6(nlh *netlink.Handle, iface netlink.Link, i *nwIface) error if i.AddressIPv6() == nil { return nil } + if err := checkRouteConflict(nlh, i.AddressIPv6(), netlink.FAMILY_V6); err != nil { + return err + } ipAddr := &netlink.Addr{IPNet: i.AddressIPv6(), Label: "", Flags: syscall.IFA_F_NODAD} return nlh.AddrAdd(iface, ipAddr) } @@ -442,3 +447,19 @@ func scanInterfaceStats(data, ifName string, i *types.InterfaceStatistics) error return err } + +func checkRouteConflict(nlh *netlink.Handle, address *net.IPNet, family int) error { + routes, err := nlh.RouteList(nil, family) + if err != nil { + return err + } + for _, route := range routes { + if route.Dst != nil { + if route.Dst.Contains(address.IP) || address.Contains(route.Dst.IP) { + return fmt.Errorf("cannot program address %v in sandbox interface because it conflicts with existing route %s", + address, route) + } + } + } + return nil +} diff --git a/libnetwork/osl/sandbox_linux_test.go b/libnetwork/osl/sandbox_linux_test.go index b76377806e..d71e24a21d 100644 --- a/libnetwork/osl/sandbox_linux_test.go +++ b/libnetwork/osl/sandbox_linux_test.go @@ -7,6 +7,7 @@ import ( "net" "os" "path/filepath" + "strings" "syscall" "testing" "time" @@ -74,8 +75,7 @@ func newInfo(hnd *netlink.Handle, t *testing.T) (Sandbox, error) { intf1.address = addr intf1.address.IP = ip4 - // ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48") - ip6, addrv6, err := net.ParseCIDR("fe80::2/64") + ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48") if err != nil { return nil, err } @@ -225,3 +225,65 @@ func TestDisableIPv6DAD(t *testing.T) { t.Fatalf("Unexpected interface flags: 0x%x. Expected to contain 0x%x", addrList[0].Flags, syscall.IFA_F_NODAD) } } + +func TestSetInterfaceIP(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + ipv4, _ := types.ParseCIDR("172.30.0.33/24") + ipv6, _ := types.ParseCIDR("2001:db8::44/64") + iface := &nwIface{address: ipv4, addressIPv6: ipv6} + + nlh, err := netlink.NewHandle(syscall.NETLINK_ROUTE) + if err != nil { + t.Fatal(err) + } + + if err := nlh.LinkAdd(&netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{Name: "sideA"}, + PeerName: "sideB", + }); err != nil { + t.Fatal(err) + } + + linkA, err := nlh.LinkByName("sideA") + if err != nil { + t.Fatal(err) + } + + linkB, err := nlh.LinkByName("sideB") + if err != nil { + t.Fatal(err) + } + + if err := nlh.LinkSetUp(linkA); err != nil { + t.Fatal(err) + } + + if err := nlh.LinkSetUp(linkB); err != nil { + t.Fatal(err) + } + + if err := setInterfaceIP(nlh, linkA, iface); err != nil { + t.Fatal(err) + } + + if err := setInterfaceIPv6(nlh, linkA, iface); err != nil { + t.Fatal(err) + } + + err = setInterfaceIP(nlh, linkB, iface) + if err == nil { + t.Fatalf("Expected route conflict error, but succeeded") + } + if !strings.Contains(err.Error(), "conflicts with existing route") { + t.Fatalf("Unexpected error: %v", err) + } + + err = setInterfaceIPv6(nlh, linkB, iface) + if err == nil { + t.Fatalf("Expected route conflict error, but succeeded") + } + if !strings.Contains(err.Error(), "conflicts with existing route") { + t.Fatalf("Unexpected error: %v", err) + } +} diff --git a/libnetwork/sandbox_test.go b/libnetwork/sandbox_test.go index 6ce245d767..76a55699ef 100644 --- a/libnetwork/sandbox_test.go +++ b/libnetwork/sandbox_test.go @@ -1,6 +1,7 @@ package libnetwork import ( + "fmt" "testing" "github.com/docker/libnetwork/config" @@ -10,7 +11,7 @@ import ( "github.com/docker/libnetwork/testutils" ) -func getTestEnv(t *testing.T, empty bool) (NetworkController, Network, Network) { +func getTestEnv(t *testing.T, numNetworks int) (NetworkController, []Network) { netType := "bridge" option := options.Generic{ @@ -28,37 +29,31 @@ func getTestEnv(t *testing.T, empty bool) (NetworkController, Network, Network) t.Fatal(err) } - if empty { - return c, nil, nil + if numNetworks == 0 { + return c, nil } - name1 := "test_nw_1" - netOption1 := options.Generic{ - netlabel.GenericData: options.Generic{ - "BridgeName": name1, - }, - } - n1, err := c.NewNetwork(netType, name1, "", NetworkOptionGeneric(netOption1)) - if err != nil { - t.Fatal(err) + nwList := make([]Network, 0, numNetworks) + for i := 0; i < numNetworks; i++ { + name := fmt.Sprintf("test_nw_%d", i) + netOption := options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": name, + }, + } + n, err := c.NewNetwork(netType, name, "", NetworkOptionGeneric(netOption)) + if err != nil { + t.Fatal(err) + } + + nwList = append(nwList, n) } - name2 := "test_nw_2" - netOption2 := options.Generic{ - netlabel.GenericData: options.Generic{ - "BridgeName": name2, - }, - } - n2, err := c.NewNetwork(netType, name2, "", NetworkOptionGeneric(netOption2)) - if err != nil { - t.Fatal(err) - } - - return c, n1, n2 + return c, nwList } func TestSandboxAddEmpty(t *testing.T) { - c, _, _ := getTestEnv(t, true) + c, _ := getTestEnv(t, 0) ctrlr := c.(*controller) sbx, err := ctrlr.NewSandbox("sandbox0") @@ -82,7 +77,7 @@ func TestSandboxAddMultiPrio(t *testing.T) { defer testutils.SetupTestOSContext(t)() } - c, nw, _ := getTestEnv(t, false) + c, nws := getTestEnv(t, 3) ctrlr := c.(*controller) sbx, err := ctrlr.NewSandbox("sandbox1") @@ -91,15 +86,15 @@ func TestSandboxAddMultiPrio(t *testing.T) { } sid := sbx.ID() - ep1, err := nw.CreateEndpoint("ep1") + ep1, err := nws[0].CreateEndpoint("ep1") if err != nil { t.Fatal(err) } - ep2, err := nw.CreateEndpoint("ep2") + ep2, err := nws[1].CreateEndpoint("ep2") if err != nil { t.Fatal(err) } - ep3, err := nw.CreateEndpoint("ep3") + ep3, err := nws[2].CreateEndpoint("ep3") if err != nil { t.Fatal(err) } @@ -163,7 +158,7 @@ func TestSandboxAddSamePrio(t *testing.T) { defer testutils.SetupTestOSContext(t)() } - c, nw1, nw2 := getTestEnv(t, false) + c, nws := getTestEnv(t, 2) ctrlr := c.(*controller) @@ -173,11 +168,11 @@ func TestSandboxAddSamePrio(t *testing.T) { } sid := sbx.ID() - ep1, err := nw1.CreateEndpoint("ep1") + ep1, err := nws[0].CreateEndpoint("ep1") if err != nil { t.Fatal(err) } - ep2, err := nw2.CreateEndpoint("ep2") + ep2, err := nws[1].CreateEndpoint("ep2") if err != nil { t.Fatal(err) }