diff --git a/libnetwork/driverapi/driverapi.go b/libnetwork/driverapi/driverapi.go index 8ae986f832..9fb41ff7b6 100644 --- a/libnetwork/driverapi/driverapi.go +++ b/libnetwork/driverapi/driverapi.go @@ -83,8 +83,8 @@ type InterfaceInfo interface { // InterfaceNameInfo provides a go interface for the drivers to assign names // to interfaces. type InterfaceNameInfo interface { - // SetNames method assigns the srcName and dstName for the interface. - SetNames(srcName, dstName string) error + // SetNames method assigns the srcName and dstPrefix for the interface. + SetNames(srcName, dstPrefix string) error // ID returns the numerical id that was assigned to the interface by the driver // CreateEndpoint. diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index 1681e126b0..b1cfe74492 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -21,7 +21,7 @@ const ( networkType = "bridge" vethPrefix = "veth" vethLen = 7 - containerVeth = "eth0" + containerVethPrefix = "eth" maxAllocatePortAttempts = 10 ifaceID = 1 ) @@ -545,7 +545,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn // Create the sandbox side pipe interface intf := &sandbox.Interface{} intf.SrcName = name2 - intf.DstName = containerVeth + intf.DstName = containerVethPrefix intf.Address = ipv4Addr if config.EnableIPv6 { diff --git a/libnetwork/endpoint.go b/libnetwork/endpoint.go index 56252ab52e..9b832358e8 100644 --- a/libnetwork/endpoint.go +++ b/libnetwork/endpoint.go @@ -291,7 +291,7 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*Contai for _, i := range ifaces { iface := &sandbox.Interface{ SrcName: i.srcName, - DstName: i.dstName, + DstName: i.dstPrefix, Address: &i.addr, } if i.addrv6.IP.To16() != nil { diff --git a/libnetwork/endpoint_info.go b/libnetwork/endpoint_info.go index 6d94741b52..f04521595a 100644 --- a/libnetwork/endpoint_info.go +++ b/libnetwork/endpoint_info.go @@ -40,12 +40,12 @@ type InterfaceInfo interface { } type endpointInterface struct { - id int - mac net.HardwareAddr - addr net.IPNet - addrv6 net.IPNet - srcName string - dstName string + id int + mac net.HardwareAddr + addr net.IPNet + addrv6 net.IPNet + srcName string + dstPrefix string } type endpointJoinInfo struct { @@ -130,9 +130,9 @@ func (i *endpointInterface) AddressIPv6() net.IPNet { return (*types.GetIPNetCopy(&i.addrv6)) } -func (i *endpointInterface) SetNames(srcName string, dstName string) error { +func (i *endpointInterface) SetNames(srcName string, dstPrefix string) error { i.srcName = srcName - i.dstName = dstName + i.dstPrefix = dstPrefix return nil } diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index cc811178ca..981128cdc6 100644 --- a/libnetwork/libnetwork_test.go +++ b/libnetwork/libnetwork_test.go @@ -23,6 +23,7 @@ import ( "github.com/docker/libnetwork/netutils" "github.com/docker/libnetwork/options" "github.com/docker/libnetwork/types" + "github.com/vishvananda/netlink" "github.com/vishvananda/netns" ) @@ -730,6 +731,35 @@ func TestNetworkQuery(t *testing.T) { const containerID = "valid_container" +func checkSandbox(t *testing.T, info libnetwork.EndpointInfo) { + origns, err := netns.Get() + if err != nil { + t.Fatalf("Could not get the current netns: %v", err) + } + defer origns.Close() + + key := info.SandboxKey() + f, err := os.OpenFile(key, os.O_RDONLY, 0) + if err != nil { + t.Fatalf("Failed to open network namespace path %q: %v", key, err) + } + defer f.Close() + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + nsFD := f.Fd() + if err = netns.Set(netns.NsHandle(nsFD)); err != nil { + t.Fatalf("Setting to the namespace pointed to by the sandbox %s failed: %v", key, err) + } + defer netns.Set(origns) + + _, err = netlink.LinkByName("eth0") + if err != nil { + t.Fatalf("Could not find the interface eth0 inside the sandbox: %v", err) + } +} + func TestEndpointJoin(t *testing.T) { if !netutils.IsRunningInContainer() { defer netutils.SetupTestNetNS(t)() @@ -786,6 +816,8 @@ func TestEndpointJoin(t *testing.T) { if info.SandboxKey() == "" { t.Fatalf("Expected an non-empty sandbox key for a joined endpoint. Instead found a empty sandbox key") } + + checkSandbox(t, info) } func TestEndpointJoinInvalidContainerId(t *testing.T) { diff --git a/libnetwork/sandbox/namespace_linux.go b/libnetwork/sandbox/namespace_linux.go index b4221f4f07..17881f1404 100644 --- a/libnetwork/sandbox/namespace_linux.go +++ b/libnetwork/sandbox/namespace_linux.go @@ -20,8 +20,10 @@ var once sync.Once // interface. It represents a linux network namespace, and moves an interface // into it when called on method AddInterface or sets the gateway etc. type networkNamespace struct { - path string - sinfo *Info + path string + sinfo *Info + nextIfIndex int + sync.Mutex } func createBasePath() { @@ -167,6 +169,11 @@ func (n *networkNamespace) RemoveInterface(i *Interface) error { } func (n *networkNamespace) AddInterface(i *Interface) error { + n.Lock() + i.DstName = fmt.Sprintf("%s%d", i.DstName, n.nextIfIndex) + n.nextIfIndex++ + n.Unlock() + runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -214,7 +221,10 @@ func (n *networkNamespace) AddInterface(i *Interface) error { return err } + n.Lock() n.sinfo.Interfaces = append(n.sinfo.Interfaces, i) + n.Unlock() + return nil } diff --git a/libnetwork/sandbox/sandbox.go b/libnetwork/sandbox/sandbox.go index 106753fd87..9e104cabdc 100644 --- a/libnetwork/sandbox/sandbox.go +++ b/libnetwork/sandbox/sandbox.go @@ -20,7 +20,9 @@ type Sandbox interface { // Add an existing Interface to this sandbox. The operation will rename // from the Interface SrcName to DstName as it moves, and reconfigure the - // interface according to the specified settings. + // interface according to the specified settings. The caller is expected + // to only provide a prefix for DstName. The AddInterface api will auto-generate + // an appropriate suffix for the DstName to disambiguate. AddInterface(*Interface) error // Remove an interface from the sandbox by renamin to original name @@ -62,7 +64,9 @@ type Interface struct { SrcName string // The name that will be assigned to the interface once moves inside a - // network namespace. + // network namespace. When the caller passes in a DstName, it is only + // expected to pass a prefix. The name will modified with an appropriately + // auto-generated suffix. DstName string // IPv4 address for the interface. diff --git a/libnetwork/sandbox/sandbox_linux_test.go b/libnetwork/sandbox/sandbox_linux_test.go index 06e98c7846..d4af061f91 100644 --- a/libnetwork/sandbox/sandbox_linux_test.go +++ b/libnetwork/sandbox/sandbox_linux_test.go @@ -15,6 +15,8 @@ import ( const ( vethName1 = "wierdlongname1" vethName2 = "wierdlongname2" + vethName3 = "wierdlongname3" + vethName4 = "wierdlongname4" sboxIfaceName = "containername" ) @@ -36,33 +38,59 @@ func newInfo(t *testing.T) (*Info, error) { veth := &netlink.Veth{ LinkAttrs: netlink.LinkAttrs{Name: vethName1, TxQLen: 0}, PeerName: vethName2} - err := netlink.LinkAdd(veth) - if err != nil { + if err := netlink.LinkAdd(veth); err != nil { return nil, err } // Store the sandbox side pipe interface // This is needed for cleanup on DeleteEndpoint() - intf := &Interface{} - intf.SrcName = vethName2 - intf.DstName = sboxIfaceName + intf1 := &Interface{} + intf1.SrcName = vethName2 + intf1.DstName = sboxIfaceName ip4, addr, err := net.ParseCIDR("192.168.1.100/24") if err != nil { return nil, err } - intf.Address = addr - intf.Address.IP = ip4 + intf1.Address = addr + intf1.Address.IP = ip4 // ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48") ip6, addrv6, err := net.ParseCIDR("fe80::2/64") if err != nil { return nil, err } - intf.AddressIPv6 = addrv6 - intf.AddressIPv6.IP = ip6 + intf1.AddressIPv6 = addrv6 + intf1.AddressIPv6.IP = ip6 - sinfo := &Info{Interfaces: []*Interface{intf}} + veth = &netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{Name: vethName3, TxQLen: 0}, + PeerName: vethName4} + + if err := netlink.LinkAdd(veth); err != nil { + return nil, err + } + + intf2 := &Interface{} + intf2.SrcName = vethName4 + intf2.DstName = sboxIfaceName + + ip4, addr, err = net.ParseCIDR("192.168.2.100/24") + if err != nil { + return nil, err + } + intf2.Address = addr + intf2.Address.IP = ip4 + + // ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48") + ip6, addrv6, err = net.ParseCIDR("fe80::3/64") + if err != nil { + return nil, err + } + intf2.AddressIPv6 = addrv6 + intf2.AddressIPv6.IP = ip6 + + sinfo := &Info{Interfaces: []*Interface{intf1, intf2}} sinfo.Gateway = net.ParseIP("192.168.1.1") // sinfo.GatewayIPv6 = net.ParseIP("2001:DB8::1") sinfo.GatewayIPv6 = net.ParseIP("fe80::1") @@ -97,7 +125,13 @@ func verifySandbox(t *testing.T, s Sandbox) { } defer netns.Set(origns) - _, err = netlink.LinkByName(sboxIfaceName) + _, err = netlink.LinkByName(sboxIfaceName + "0") + if err != nil { + t.Fatalf("Could not find the interface %s inside the sandbox: %v", sboxIfaceName, + err) + } + + _, err = netlink.LinkByName(sboxIfaceName + "1") if err != nil { t.Fatalf("Could not find the interface %s inside the sandbox: %v", sboxIfaceName, err)