From 89045ca3816432a97f3323933d43f70d93c179cd Mon Sep 17 00:00:00 2001 From: Jana Radhakrishnan Date: Thu, 21 May 2015 18:04:49 +0000 Subject: [PATCH] Modify driver Join api to only allow dst prefix Currently the driver api allows the driver to specify the full interface name for the interface inside the container. This is not appropriate since the driver does not have the full view of the sandbox to correcly allocate an unambiguous interface name. Instead with this PR the driver will be allowed to specify a prefix for the name and libnetwork and sandbox layers will disambiguate it with an appropriate suffix. Signed-off-by: Jana Radhakrishnan --- libnetwork/driverapi/driverapi.go | 4 +- libnetwork/drivers/bridge/bridge.go | 4 +- libnetwork/endpoint.go | 2 +- libnetwork/endpoint_info.go | 16 +++---- libnetwork/libnetwork_test.go | 32 ++++++++++++++ libnetwork/sandbox/namespace_linux.go | 14 +++++- libnetwork/sandbox/sandbox.go | 8 +++- libnetwork/sandbox/sandbox_linux_test.go | 56 +++++++++++++++++++----- 8 files changed, 108 insertions(+), 28 deletions(-) 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)