Merge pull request #257 from mrjana/overlay

Refactor sandbox code to use interfaces and add bridge support to sandbox
This commit is contained in:
aboch 2015-06-08 14:05:59 -07:00
commit d861b7ec70
13 changed files with 767 additions and 731 deletions

View File

@ -5,7 +5,6 @@ import (
"net"
"os/exec"
"strconv"
"strings"
"sync"
"github.com/Sirupsen/logrus"
@ -15,7 +14,6 @@ import (
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/options"
"github.com/docker/libnetwork/portmapper"
"github.com/docker/libnetwork/sandbox"
"github.com/docker/libnetwork/types"
"github.com/vishvananda/netlink"
)
@ -71,7 +69,9 @@ type containerConfiguration struct {
type bridgeEndpoint struct {
id types.UUID
intf *sandbox.Interface
srcName string
addr *net.IPNet
addrv6 *net.IPNet
macAddress net.HardwareAddr
config *endpointConfiguration // User specified parameters
containerConfig *containerConfiguration
@ -701,13 +701,13 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
}()
// Generate a name for what will be the host side pipe interface
name1, err := generateIfaceName()
name1, err := netutils.GenerateIfaceName(vethPrefix, vethLen)
if err != nil {
return err
}
// Generate a name for what will be the sandbox side pipe interface
name2, err := generateIfaceName()
name2, err := netutils.GenerateIfaceName(vethPrefix, vethLen)
if err != nil {
return err
}
@ -803,25 +803,20 @@ 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 = containerVethPrefix
intf.Address = ipv4Addr
endpoint.srcName = name2
endpoint.addr = ipv4Addr
if config.EnableIPv6 {
intf.AddressIPv6 = ipv6Addr
endpoint.addrv6 = ipv6Addr
}
// Store the interface in endpoint, this is needed for cleanup on DeleteEndpoint()
endpoint.intf = intf
err = epInfo.AddInterface(ifaceID, endpoint.macAddress, *ipv4Addr, *ipv6Addr)
if err != nil {
return err
}
// Program any required port mapping and store them in the endpoint
endpoint.portMapping, err = n.allocatePorts(epConfig, intf, config.DefaultBindingIP, config.EnableUserlandProxy)
endpoint.portMapping, err = n.allocatePorts(epConfig, endpoint, config.DefaultBindingIP, config.EnableUserlandProxy)
if err != nil {
return err
}
@ -883,14 +878,14 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
n.releasePorts(ep)
// Release the v4 address allocated to this endpoint's sandbox interface
err = ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, ep.intf.Address.IP)
err = ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, ep.addr.IP)
if err != nil {
return err
}
// Release the v6 address allocated to this endpoint's sandbox interface
if config.EnableIPv6 {
err := ipAllocator.ReleaseIP(n.bridge.bridgeIPv6, ep.intf.AddressIPv6.IP)
err := ipAllocator.ReleaseIP(n.bridge.bridgeIPv6, ep.addrv6.IP)
if err != nil {
return err
}
@ -898,7 +893,7 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
// Try removal of link. Discard error: link pair might have
// already been deleted by sandbox delete.
link, err := netlink.LinkByName(ep.intf.SrcName)
link, err := netlink.LinkByName(ep.srcName)
if err == nil {
netlink.LinkDel(link)
}
@ -982,7 +977,7 @@ func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinI
for _, iNames := range jinfo.InterfaceNames() {
// Make sure to set names on the correct interface ID.
if iNames.ID() == ifaceID {
err = iNames.SetNames(endpoint.intf.SrcName, endpoint.intf.DstName)
err = iNames.SetNames(endpoint.srcName, containerVethPrefix)
if err != nil {
return err
}
@ -1060,8 +1055,8 @@ func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options
return err
}
l := newLink(parentEndpoint.intf.Address.IP.String(),
endpoint.intf.Address.IP.String(),
l := newLink(parentEndpoint.addr.IP.String(),
endpoint.addr.IP.String(),
endpoint.config.ExposedPorts, network.config.BridgeName)
if enable {
err = l.Enable()
@ -1093,8 +1088,8 @@ func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options
continue
}
l := newLink(endpoint.intf.Address.IP.String(),
childEndpoint.intf.Address.IP.String(),
l := newLink(endpoint.addr.IP.String(),
childEndpoint.addr.IP.String(),
childEndpoint.config.ExposedPorts, network.config.BridgeName)
if enable {
err = l.Enable()
@ -1184,22 +1179,3 @@ func electMacAddress(epConfig *endpointConfiguration) net.HardwareAddr {
}
return netutils.GenerateRandomMAC()
}
// Generates a name to be used for a virtual ethernet
// interface. The name is constructed by 'veth' appended
// by a randomly generated hex value. (example: veth0f60e2c)
func generateIfaceName() (string, error) {
for i := 0; i < 3; i++ {
name, err := netutils.GenerateRandomName(vethPrefix, vethLen)
if err != nil {
continue
}
if _, err := net.InterfaceByName(name); err != nil {
if strings.Contains(err.Error(), "no such") {
return name, nil
}
return "", err
}
}
return "", &ErrIfaceName{}
}

View File

@ -7,7 +7,6 @@ import (
"net"
"github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/sandbox"
"github.com/docker/libnetwork/types"
)
@ -15,7 +14,7 @@ var (
defaultBindingIP = net.IPv4(0, 0, 0, 0)
)
func (n *bridgeNetwork) allocatePorts(epConfig *endpointConfiguration, intf *sandbox.Interface, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
func (n *bridgeNetwork) allocatePorts(epConfig *endpointConfiguration, ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
if epConfig == nil || epConfig.PortBindings == nil {
return nil, nil
}
@ -25,7 +24,7 @@ func (n *bridgeNetwork) allocatePorts(epConfig *endpointConfiguration, intf *san
defHostIP = reqDefBindIP
}
return n.allocatePortsInternal(epConfig.PortBindings, intf.Address.IP, defHostIP, ulPxyEnabled)
return n.allocatePortsInternal(epConfig.PortBindings, ep.addr.IP, defHostIP, ulPxyEnabled)
}
func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {

View File

@ -9,7 +9,9 @@ import (
"fmt"
"io"
"net"
"strings"
"github.com/docker/libnetwork/types"
"github.com/vishvananda/netlink"
)
@ -147,3 +149,22 @@ func GenerateRandomName(prefix string, size int) (string, error) {
}
return prefix + hex.EncodeToString(id)[:size], nil
}
// GenerateIfaceName returns an interface name using the passed in
// prefix and the length of random bytes. The api ensures that the
// there are is no interface which exists with that name.
func GenerateIfaceName(prefix string, len int) (string, error) {
for i := 0; i < 3; i++ {
name, err := GenerateRandomName(prefix, len)
if err != nil {
continue
}
if _, err := net.InterfaceByName(name); err != nil {
if strings.Contains(err.Error(), "no such") {
return name, nil
}
return "", err
}
}
return "", types.InternalErrorf("could not generate interface name")
}

View File

@ -1,176 +0,0 @@
package sandbox
import (
"fmt"
"net"
"os"
"runtime"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
)
func configureInterface(iface netlink.Link, settings *Interface) error {
ifaceName := iface.Attrs().Name
ifaceConfigurators := []struct {
Fn func(netlink.Link, *Interface) error
ErrMessage string
}{
{setInterfaceName, fmt.Sprintf("error renaming interface %q to %q", ifaceName, settings.DstName)},
{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)},
{setInterfaceRoutes, fmt.Sprintf("error setting interface %q routes to %q", ifaceName, settings.Routes)},
}
for _, config := range ifaceConfigurators {
if err := config.Fn(iface, settings); err != nil {
return fmt.Errorf("%s: %v", config.ErrMessage, err)
}
}
return nil
}
func programGateway(path string, gw net.IP, isAdd bool) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
origns, err := netns.Get()
if err != nil {
return err
}
defer origns.Close()
f, err := os.OpenFile(path, os.O_RDONLY, 0)
if err != nil {
return fmt.Errorf("failed get network namespace %q: %v", path, err)
}
defer f.Close()
nsFD := f.Fd()
if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
return err
}
defer netns.Set(origns)
gwRoutes, err := netlink.RouteGet(gw)
if err != nil {
return fmt.Errorf("route for the gateway could not be found: %v", err)
}
if isAdd {
return netlink.RouteAdd(&netlink.Route{
Scope: netlink.SCOPE_UNIVERSE,
LinkIndex: gwRoutes[0].LinkIndex,
Gw: gw,
})
}
return netlink.RouteDel(&netlink.Route{
Scope: netlink.SCOPE_UNIVERSE,
LinkIndex: gwRoutes[0].LinkIndex,
Gw: gw,
})
}
// Program a route in to the namespace routing table.
func programRoute(path string, dest *net.IPNet, nh net.IP) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
origns, err := netns.Get()
if err != nil {
return err
}
defer origns.Close()
f, err := os.OpenFile(path, os.O_RDONLY, 0)
if err != nil {
return fmt.Errorf("failed get network namespace %q: %v", path, err)
}
defer f.Close()
nsFD := f.Fd()
if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
return err
}
defer netns.Set(origns)
gwRoutes, err := netlink.RouteGet(nh)
if err != nil {
return fmt.Errorf("route for the next hop could not be found: %v", err)
}
return netlink.RouteAdd(&netlink.Route{
Scope: netlink.SCOPE_UNIVERSE,
LinkIndex: gwRoutes[0].LinkIndex,
Gw: gwRoutes[0].Gw,
Dst: dest,
})
}
// Delete a route from the namespace routing table.
func removeRoute(path string, dest *net.IPNet, nh net.IP) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
origns, err := netns.Get()
if err != nil {
return err
}
defer origns.Close()
f, err := os.OpenFile(path, os.O_RDONLY, 0)
if err != nil {
return fmt.Errorf("failed get network namespace %q: %v", path, err)
}
defer f.Close()
nsFD := f.Fd()
if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
return err
}
defer netns.Set(origns)
gwRoutes, err := netlink.RouteGet(nh)
if err != nil {
return fmt.Errorf("route for the next hop could not be found: %v", err)
}
return netlink.RouteDel(&netlink.Route{
Scope: netlink.SCOPE_UNIVERSE,
LinkIndex: gwRoutes[0].LinkIndex,
Gw: gwRoutes[0].Gw,
Dst: dest,
})
}
func setInterfaceIP(iface netlink.Link, settings *Interface) error {
ipAddr := &netlink.Addr{IPNet: settings.Address, Label: ""}
return netlink.AddrAdd(iface, ipAddr)
}
func setInterfaceIPv6(iface netlink.Link, settings *Interface) error {
if settings.AddressIPv6 == nil {
return nil
}
ipAddr := &netlink.Addr{IPNet: settings.AddressIPv6, Label: ""}
return netlink.AddrAdd(iface, ipAddr)
}
func setInterfaceName(iface netlink.Link, settings *Interface) error {
return netlink.LinkSetName(iface, settings.DstName)
}
func setInterfaceRoutes(iface netlink.Link, settings *Interface) error {
for _, route := range settings.Routes {
err := netlink.RouteAdd(&netlink.Route{
Scope: netlink.SCOPE_LINK,
LinkIndex: iface.Attrs().Index,
Dst: route,
})
if err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,313 @@
package sandbox
import (
"fmt"
"net"
"sync"
"github.com/docker/libnetwork/types"
"github.com/vishvananda/netlink"
)
// IfaceOption is a function option type to set interface options
type IfaceOption func(i *nwIface)
type nwIface struct {
srcName string
dstName string
master string
dstMaster string
address *net.IPNet
addressIPv6 *net.IPNet
routes []*net.IPNet
bridge bool
ns *networkNamespace
sync.Mutex
}
func (i *nwIface) SrcName() string {
i.Lock()
defer i.Unlock()
return i.srcName
}
func (i *nwIface) DstName() string {
i.Lock()
defer i.Unlock()
return i.dstName
}
func (i *nwIface) DstMaster() string {
i.Lock()
defer i.Unlock()
return i.dstMaster
}
func (i *nwIface) Bridge() bool {
i.Lock()
defer i.Unlock()
return i.bridge
}
func (i *nwIface) Master() string {
i.Lock()
defer i.Unlock()
return i.master
}
func (i *nwIface) Address() *net.IPNet {
i.Lock()
defer i.Unlock()
return types.GetIPNetCopy(i.address)
}
func (i *nwIface) AddressIPv6() *net.IPNet {
i.Lock()
defer i.Unlock()
return types.GetIPNetCopy(i.addressIPv6)
}
func (i *nwIface) Routes() []*net.IPNet {
i.Lock()
defer i.Unlock()
routes := make([]*net.IPNet, len(i.routes))
for index, route := range i.routes {
r := types.GetIPNetCopy(route)
routes[index] = r
}
return routes
}
func (n *networkNamespace) Interfaces() []Interface {
n.Lock()
defer n.Unlock()
ifaces := make([]Interface, len(n.iFaces))
for i, iface := range n.iFaces {
ifaces[i] = iface
}
return ifaces
}
func (i *nwIface) Remove() error {
i.Lock()
n := i.ns
i.Unlock()
n.Lock()
path := n.path
n.Unlock()
return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
// Find the network inteerface identified by the DstName attribute.
iface, err := netlink.LinkByName(i.DstName())
if err != nil {
return err
}
// Down the interface before configuring
if err := netlink.LinkSetDown(iface); err != nil {
return err
}
err = netlink.LinkSetName(iface, i.SrcName())
if err != nil {
fmt.Println("LinkSetName failed: ", err)
return err
}
// if it is a bridge just delete it.
if i.Bridge() {
if err := netlink.LinkDel(iface); err != nil {
return fmt.Errorf("failed deleting bridge %q: %v", i.SrcName(), err)
}
} else {
// Move the network interface to caller namespace.
if err := netlink.LinkSetNsFd(iface, callerFD); err != nil {
fmt.Println("LinkSetNsPid failed: ", err)
return err
}
}
n.Lock()
for index, intf := range n.iFaces {
if intf == i {
n.iFaces = append(n.iFaces[:index], n.iFaces[index+1:]...)
break
}
}
n.Unlock()
return nil
})
}
func (n *networkNamespace) findDstMaster(srcName string) string {
n.Lock()
defer n.Unlock()
for _, i := range n.iFaces {
// The master should match the srcname of the interface and the
// master interface should be of type bridge.
if i.SrcName() == srcName && i.Bridge() {
return i.DstName()
}
}
return ""
}
func (n *networkNamespace) AddInterface(srcName, dstPrefix string, options ...IfaceOption) error {
i := &nwIface{srcName: srcName, dstName: dstPrefix, ns: n}
i.processInterfaceOptions(options...)
if i.master != "" {
i.dstMaster = n.findDstMaster(i.master)
if i.dstMaster == "" {
return fmt.Errorf("could not find an appropriate master %q for %q",
i.master, i.srcName)
}
}
n.Lock()
i.dstName = fmt.Sprintf("%s%d", i.dstName, n.nextIfIndex)
n.nextIfIndex++
path := n.path
n.Unlock()
return nsInvoke(path, func(nsFD int) error {
// If it is a bridge interface we have to create the bridge inside
// the namespace so don't try to lookup the interface using srcName
if i.bridge {
return nil
}
// Find the network interface identified by the SrcName attribute.
iface, err := netlink.LinkByName(i.srcName)
if err != nil {
return fmt.Errorf("failed to get link by name %q: %v", i.srcName, err)
}
// Move the network interface to the destination namespace.
if err := netlink.LinkSetNsFd(iface, nsFD); err != nil {
return fmt.Errorf("failed to set namespace on link %q: %v", i.srcName, err)
}
return nil
}, func(callerFD int) error {
if i.bridge {
link := &netlink.Bridge{
LinkAttrs: netlink.LinkAttrs{
Name: i.srcName,
},
}
if err := netlink.LinkAdd(link); err != nil {
return fmt.Errorf("failed to create bridge %q: %v", i.srcName, err)
}
}
// Find the network interface identified by the SrcName attribute.
iface, err := netlink.LinkByName(i.srcName)
if err != nil {
return fmt.Errorf("failed to get link by name %q: %v", i.srcName, err)
}
// Down the interface before configuring
if err := netlink.LinkSetDown(iface); err != nil {
return fmt.Errorf("failed to set link down: %v", err)
}
// Configure the interface now this is moved in the proper namespace.
if err := configureInterface(iface, i); err != nil {
return err
}
// Up the interface.
if err := netlink.LinkSetUp(iface); err != nil {
return fmt.Errorf("failed to set link up: %v", err)
}
n.Lock()
n.iFaces = append(n.iFaces, i)
n.Unlock()
return nil
})
}
func configureInterface(iface netlink.Link, i *nwIface) error {
ifaceName := iface.Attrs().Name
ifaceConfigurators := []struct {
Fn func(netlink.Link, *nwIface) error
ErrMessage string
}{
{setInterfaceName, fmt.Sprintf("error renaming interface %q to %q", ifaceName, i.DstName())},
{setInterfaceIP, fmt.Sprintf("error setting interface %q IP to %q", ifaceName, i.Address())},
{setInterfaceIPv6, fmt.Sprintf("error setting interface %q IPv6 to %q", ifaceName, i.AddressIPv6())},
{setInterfaceRoutes, fmt.Sprintf("error setting interface %q routes to %q", ifaceName, i.Routes())},
{setInterfaceMaster, fmt.Sprintf("error setting interface %q master to %q", ifaceName, i.DstMaster())},
}
for _, config := range ifaceConfigurators {
if err := config.Fn(iface, i); err != nil {
return fmt.Errorf("%s: %v", config.ErrMessage, err)
}
}
return nil
}
func setInterfaceMaster(iface netlink.Link, i *nwIface) error {
if i.DstMaster() == "" {
return nil
}
return netlink.LinkSetMaster(iface, &netlink.Bridge{
LinkAttrs: netlink.LinkAttrs{Name: i.DstMaster()}})
}
func setInterfaceIP(iface netlink.Link, i *nwIface) error {
if i.Address() == nil {
return nil
}
ipAddr := &netlink.Addr{IPNet: i.Address(), Label: ""}
return netlink.AddrAdd(iface, ipAddr)
}
func setInterfaceIPv6(iface netlink.Link, i *nwIface) error {
if i.AddressIPv6() == nil {
return nil
}
ipAddr := &netlink.Addr{IPNet: i.AddressIPv6(), Label: ""}
return netlink.AddrAdd(iface, ipAddr)
}
func setInterfaceName(iface netlink.Link, i *nwIface) error {
return netlink.LinkSetName(iface, i.DstName())
}
func setInterfaceRoutes(iface netlink.Link, i *nwIface) error {
for _, route := range i.Routes() {
err := netlink.RouteAdd(&netlink.Route{
Scope: netlink.SCOPE_LINK,
LinkIndex: iface.Attrs().Index,
Dst: route,
})
if err != nil {
return err
}
}
return nil
}

View File

@ -32,9 +32,12 @@ var (
// 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
nextIfIndex int
path string
iFaces []*nwIface
gw net.IP
gwv6 net.IP
staticRoutes []*types.StaticRoute
nextIfIndex int
sync.Mutex
}
@ -127,12 +130,16 @@ func GenerateKey(containerID string) string {
// NewSandbox provides a new sandbox instance created in an os specific way
// provided a key which uniquely identifies the sandbox
func NewSandbox(key string, osCreate bool) (Sandbox, error) {
info, err := createNetworkNamespace(key, osCreate)
err := createNetworkNamespace(key, osCreate)
if err != nil {
return nil, err
}
return &networkNamespace{path: key, sinfo: info}, nil
return &networkNamespace{path: key}, nil
}
func (n *networkNamespace) InterfaceOptions() IfaceOptionSetter {
return n
}
func reexecCreateNamespace() {
@ -149,18 +156,18 @@ func reexecCreateNamespace() {
}
}
func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
func createNetworkNamespace(path string, osCreate bool) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
origns, err := netns.Get()
if err != nil {
return nil, err
return err
}
defer origns.Close()
if err := createNamespaceFile(path); err != nil {
return nil, err
return err
}
cmd := &exec.Cmd{
@ -174,12 +181,10 @@ func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNET
}
if err := cmd.Run(); err != nil {
return nil, fmt.Errorf("namespace creation reexec command failed: %v", err)
return fmt.Errorf("namespace creation reexec command failed: %v", err)
}
interfaces := []*Interface{}
info := &Info{Interfaces: interfaces}
return info, nil
return nil
}
func unmountNamespaceFile(path string) {
@ -217,7 +222,7 @@ func loopbackUp() error {
return netlink.LinkSetUp(iface)
}
func (n *networkNamespace) RemoveInterface(i *Interface) error {
func nsInvoke(path string, prefunc func(nsFD int) error, postfunc func(callerFD int) error) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
@ -227,84 +232,18 @@ func (n *networkNamespace) RemoveInterface(i *Interface) error {
}
defer origns.Close()
f, err := os.OpenFile(n.path, os.O_RDONLY, 0)
f, err := os.OpenFile(path, os.O_RDONLY, 0)
if err != nil {
return fmt.Errorf("failed get network namespace %q: %v", n.path, err)
return fmt.Errorf("failed get network namespace %q: %v", path, err)
}
defer f.Close()
nsFD := f.Fd()
if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
return err
}
defer netns.Set(origns)
// Find the network inteerface identified by the DstName attribute.
iface, err := netlink.LinkByName(i.DstName)
if err != nil {
return err
}
// Down the interface before configuring
if err := netlink.LinkSetDown(iface); err != nil {
return err
}
err = netlink.LinkSetName(iface, i.SrcName)
if err != nil {
fmt.Println("LinkSetName failed: ", err)
return err
}
// Move the network interface to caller namespace.
if err := netlink.LinkSetNsFd(iface, int(origns)); err != nil {
fmt.Println("LinkSetNsPid failed: ", err)
return err
}
n.Lock()
for index, intf := range n.sinfo.Interfaces {
if intf == i {
n.sinfo.Interfaces = append(n.sinfo.Interfaces[:index], n.sinfo.Interfaces[index+1:]...)
break
}
}
n.Unlock()
return nil
}
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()
origns, err := netns.Get()
if err != nil {
return err
}
defer origns.Close()
f, err := os.OpenFile(n.path, os.O_RDONLY, 0)
if err != nil {
return fmt.Errorf("failed get network namespace %q: %v", n.path, err)
}
defer f.Close()
// Find the network interface identified by the SrcName attribute.
iface, err := netlink.LinkByName(i.SrcName)
if err != nil {
return err
}
// Move the network interface to the destination namespace.
nsFD := f.Fd()
if err := netlink.LinkSetNsFd(iface, int(nsFD)); err != nil {
return err
// Invoked before the namespace switch happens but after the namespace file
// handle is obtained.
if err := prefunc(int(nsFD)); err != nil {
return fmt.Errorf("failed in prefunc: %v", err)
}
if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
@ -312,133 +251,19 @@ func (n *networkNamespace) AddInterface(i *Interface) error {
}
defer netns.Set(origns)
// Down the interface before configuring
if err := netlink.LinkSetDown(iface); err != nil {
return err
}
// Configure the interface now this is moved in the proper namespace.
if err := configureInterface(iface, i); err != nil {
return err
}
// Up the interface.
if err := netlink.LinkSetUp(iface); err != nil {
return err
}
n.Lock()
n.sinfo.Interfaces = append(n.sinfo.Interfaces, i)
n.Unlock()
return nil
// Invoked after the namespace switch.
return postfunc(int(origns))
}
func (n *networkNamespace) SetGateway(gw net.IP) error {
// Silently return if the gateway is empty
if len(gw) == 0 {
return nil
}
err := programGateway(n.path, gw, true)
if err == nil {
n.Lock()
n.sinfo.Gateway = gw
n.Unlock()
}
return err
}
func (n *networkNamespace) UnsetGateway() error {
n.Lock()
gw := n.sinfo.Gateway
n.Unlock()
// Silently return if the gateway is empty
if len(gw) == 0 {
return nil
}
err := programGateway(n.path, gw, false)
if err == nil {
n.Lock()
n.sinfo.Gateway = net.IP{}
n.Unlock()
}
return err
}
func (n *networkNamespace) SetGatewayIPv6(gw net.IP) error {
// Silently return if the gateway is empty
if len(gw) == 0 {
return nil
}
err := programGateway(n.path, gw, true)
if err == nil {
n.Lock()
n.sinfo.GatewayIPv6 = gw
n.Unlock()
}
return err
}
func (n *networkNamespace) UnsetGatewayIPv6() error {
n.Lock()
gw := n.sinfo.GatewayIPv6
n.Unlock()
// Silently return if the gateway is empty
if len(gw) == 0 {
return nil
}
err := programGateway(n.path, gw, false)
if err == nil {
n.Lock()
n.sinfo.GatewayIPv6 = net.IP{}
n.Unlock()
}
return err
}
func (n *networkNamespace) AddStaticRoute(r *types.StaticRoute) error {
err := programRoute(n.path, r.Destination, r.NextHop)
if err == nil {
n.Lock()
n.sinfo.StaticRoutes = append(n.sinfo.StaticRoutes, r)
n.Unlock()
}
return err
}
func (n *networkNamespace) RemoveStaticRoute(r *types.StaticRoute) error {
err := removeRoute(n.path, r.Destination, r.NextHop)
if err == nil {
n.Lock()
lastIndex := len(n.sinfo.StaticRoutes) - 1
for i, v := range n.sinfo.StaticRoutes {
if v == r {
// Overwrite the route we're removing with the last element
n.sinfo.StaticRoutes[i] = n.sinfo.StaticRoutes[lastIndex]
// Shorten the slice to trim the extra element
n.sinfo.StaticRoutes = n.sinfo.StaticRoutes[:lastIndex]
break
}
}
n.Unlock()
}
return err
}
func (n *networkNamespace) Interfaces() []*Interface {
func (n *networkNamespace) nsPath() string {
n.Lock()
defer n.Unlock()
return n.sinfo.Interfaces
return n.path
}
func (n *networkNamespace) Info() Info {
return n
}
func (n *networkNamespace) Key() string {

View File

@ -0,0 +1,41 @@
package sandbox
import "net"
func (i *nwIface) processInterfaceOptions(options ...IfaceOption) {
for _, opt := range options {
if opt != nil {
opt(i)
}
}
}
func (n *networkNamespace) Bridge(isBridge bool) IfaceOption {
return func(i *nwIface) {
i.bridge = isBridge
}
}
func (n *networkNamespace) Master(name string) IfaceOption {
return func(i *nwIface) {
i.master = name
}
}
func (n *networkNamespace) Address(addr *net.IPNet) IfaceOption {
return func(i *nwIface) {
i.address = addr
}
}
func (n *networkNamespace) AddressIPv6(addr *net.IPNet) IfaceOption {
return func(i *nwIface) {
i.addressIPv6 = addr
}
}
func (n *networkNamespace) Routes(routes []*net.IPNet) IfaceOption {
return func(i *nwIface) {
i.routes = routes
}
}

View File

@ -0,0 +1,198 @@
package sandbox
import (
"fmt"
"net"
"github.com/docker/libnetwork/types"
"github.com/vishvananda/netlink"
)
func (n *networkNamespace) Gateway() net.IP {
n.Lock()
defer n.Unlock()
return n.gw
}
func (n *networkNamespace) GatewayIPv6() net.IP {
n.Lock()
defer n.Unlock()
return n.gwv6
}
func (n *networkNamespace) StaticRoutes() []*types.StaticRoute {
n.Lock()
defer n.Unlock()
routes := make([]*types.StaticRoute, len(n.staticRoutes))
for i, route := range n.staticRoutes {
r := route.GetCopy()
routes[i] = r
}
return routes
}
func (n *networkNamespace) setGateway(gw net.IP) {
n.Lock()
n.gw = gw
n.Unlock()
}
func (n *networkNamespace) setGatewayIPv6(gwv6 net.IP) {
n.Lock()
n.gwv6 = gwv6
n.Unlock()
}
func (n *networkNamespace) SetGateway(gw net.IP) error {
// Silently return if the gateway is empty
if len(gw) == 0 {
return nil
}
err := programGateway(n.nsPath(), gw, true)
if err == nil {
n.setGateway(gw)
}
return err
}
func (n *networkNamespace) UnsetGateway() error {
gw := n.Gateway()
// Silently return if the gateway is empty
if len(gw) == 0 {
return nil
}
err := programGateway(n.nsPath(), gw, false)
if err == nil {
n.setGateway(net.IP{})
}
return err
}
func programGateway(path string, gw net.IP, isAdd bool) error {
return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
gwRoutes, err := netlink.RouteGet(gw)
if err != nil {
return fmt.Errorf("route for the gateway could not be found: %v", err)
}
if isAdd {
return netlink.RouteAdd(&netlink.Route{
Scope: netlink.SCOPE_UNIVERSE,
LinkIndex: gwRoutes[0].LinkIndex,
Gw: gw,
})
}
return netlink.RouteDel(&netlink.Route{
Scope: netlink.SCOPE_UNIVERSE,
LinkIndex: gwRoutes[0].LinkIndex,
Gw: gw,
})
})
}
// Program a route in to the namespace routing table.
func programRoute(path string, dest *net.IPNet, nh net.IP) error {
return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
gwRoutes, err := netlink.RouteGet(nh)
if err != nil {
return fmt.Errorf("route for the next hop could not be found: %v", err)
}
return netlink.RouteAdd(&netlink.Route{
Scope: netlink.SCOPE_UNIVERSE,
LinkIndex: gwRoutes[0].LinkIndex,
Gw: gwRoutes[0].Gw,
Dst: dest,
})
})
}
// Delete a route from the namespace routing table.
func removeRoute(path string, dest *net.IPNet, nh net.IP) error {
return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
gwRoutes, err := netlink.RouteGet(nh)
if err != nil {
return fmt.Errorf("route for the next hop could not be found: %v", err)
}
return netlink.RouteDel(&netlink.Route{
Scope: netlink.SCOPE_UNIVERSE,
LinkIndex: gwRoutes[0].LinkIndex,
Gw: gwRoutes[0].Gw,
Dst: dest,
})
})
}
func (n *networkNamespace) SetGatewayIPv6(gwv6 net.IP) error {
// Silently return if the gateway is empty
if len(gwv6) == 0 {
return nil
}
err := programGateway(n.nsPath(), gwv6, true)
if err == nil {
n.SetGatewayIPv6(gwv6)
}
return err
}
func (n *networkNamespace) UnsetGatewayIPv6() error {
gwv6 := n.GatewayIPv6()
// Silently return if the gateway is empty
if len(gwv6) == 0 {
return nil
}
err := programGateway(n.nsPath(), gwv6, false)
if err == nil {
n.Lock()
n.gwv6 = net.IP{}
n.Unlock()
}
return err
}
func (n *networkNamespace) AddStaticRoute(r *types.StaticRoute) error {
err := programRoute(n.nsPath(), r.Destination, r.NextHop)
if err == nil {
n.Lock()
n.staticRoutes = append(n.staticRoutes, r)
n.Unlock()
}
return err
}
func (n *networkNamespace) RemoveStaticRoute(r *types.StaticRoute) error {
n.Lock()
err := removeRoute(n.nsPath(), r.Destination, r.NextHop)
if err == nil {
n.Lock()
lastIndex := len(n.staticRoutes) - 1
for i, v := range n.staticRoutes {
if v == r {
// Overwrite the route we're removing with the last element
n.staticRoutes[i] = n.staticRoutes[lastIndex]
// Shorten the slice to trim the extra element
n.staticRoutes = n.staticRoutes[:lastIndex]
break
}
}
n.Unlock()
}
return err
}

View File

@ -12,22 +12,12 @@ type Sandbox interface {
// The path where the network namespace is mounted.
Key() string
// The collection of Interface previously added with the AddInterface
// method. Note that this doesn't incude network interfaces added in any
// other way (such as the default loopback interface which are automatically
// created on creation of a sandbox).
Interfaces() []*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. 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 renaming to original name
// and moving it out of the sandbox.
RemoveInterface(*Interface) error
AddInterface(SrcName string, DstPrefix string, options ...IfaceOption) error
// Set default IPv4 gateway for the sandbox
SetGateway(gw net.IP) error
@ -47,25 +37,55 @@ type Sandbox interface {
// Remove a static route from the sandbox.
RemoveStaticRoute(*types.StaticRoute) error
// Returns an interface with methods to set interface options.
InterfaceOptions() IfaceOptionSetter
// Returns an interface with methods to get sandbox state.
Info() Info
// Destroy the sandbox
Destroy() error
}
// IfaceOptionSetter interface defines the option setter methods for interface options.
type IfaceOptionSetter interface {
// Bridge returns an option setter to set if the interface is a bridge.
Bridge(bool) IfaceOption
// Address returns an option setter to set IPv4 address.
Address(*net.IPNet) IfaceOption
// Address returns an option setter to set IPv6 address.
AddressIPv6(*net.IPNet) IfaceOption
// Master returns an option setter to set the master interface if any for this
// interface. The master interface name should refer to the srcname of a
// previously added interface of type bridge.
Master(string) IfaceOption
// Address returns an option setter to set interface routes.
Routes([]*net.IPNet) IfaceOption
}
// Info represents all possible information that
// the driver wants to place in the sandbox which includes
// interfaces, routes and gateway
type Info struct {
Interfaces []*Interface
type Info interface {
// The collection of Interface previously added with the AddInterface
// method. Note that this doesn't incude network interfaces added in any
// other way (such as the default loopback interface which are automatically
// created on creation of a sandbox).
Interfaces() []Interface
// IPv4 gateway for the sandbox.
Gateway net.IP
Gateway() net.IP
// IPv6 gateway for the sandbox.
GatewayIPv6 net.IP
GatewayIPv6() net.IP
// Additional static routes for the sandbox. (Note that directly
// connected routes are stored on the particular interface they refer to.)
StaticRoutes []*types.StaticRoute
StaticRoutes() []*types.StaticRoute
// TODO: Add ip tables etc.
}
@ -75,140 +95,32 @@ type Info struct {
// caller to use this information when moving interface SrcName from host
// namespace to DstName in a different net namespace with the appropriate
// network settings.
type Interface struct {
type Interface interface {
// The name of the interface in the origin network namespace.
SrcName string
SrcName() string
// The name that will be assigned to the interface once moves inside a
// 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
DstName() string
// IPv4 address for the interface.
Address *net.IPNet
Address() *net.IPNet
// IPv6 address for the interface.
AddressIPv6 *net.IPNet
AddressIPv6() *net.IPNet
// IP routes for the interface.
Routes []*net.IPNet
}
// GetCopy returns a copy of this Interface structure
func (i *Interface) GetCopy() *Interface {
copiedRoutes := make([]*net.IPNet, len(i.Routes))
for index := range i.Routes {
copiedRoutes[index] = types.GetIPNetCopy(i.Routes[index])
}
return &Interface{
SrcName: i.SrcName,
DstName: i.DstName,
Address: types.GetIPNetCopy(i.Address),
AddressIPv6: types.GetIPNetCopy(i.AddressIPv6),
Routes: copiedRoutes,
}
}
// Equal checks if this instance of Interface is equal to the passed one
func (i *Interface) Equal(o *Interface) bool {
if i == o {
return true
}
if o == nil {
return false
}
if i.SrcName != o.SrcName || i.DstName != o.DstName {
return false
}
if !types.CompareIPNet(i.Address, o.Address) {
return false
}
if !types.CompareIPNet(i.AddressIPv6, o.AddressIPv6) {
return false
}
if len(i.Routes) != len(o.Routes) {
return false
}
for index := range i.Routes {
if !types.CompareIPNet(i.Routes[index], o.Routes[index]) {
return false
}
}
return true
}
// GetCopy returns a copy of this SandboxInfo structure
func (s *Info) GetCopy() *Info {
list := make([]*Interface, len(s.Interfaces))
for i, iface := range s.Interfaces {
list[i] = iface.GetCopy()
}
gw := types.GetIPCopy(s.Gateway)
gw6 := types.GetIPCopy(s.GatewayIPv6)
routes := make([]*types.StaticRoute, len(s.StaticRoutes))
for i, r := range s.StaticRoutes {
routes[i] = r.GetCopy()
}
return &Info{Interfaces: list,
Gateway: gw,
GatewayIPv6: gw6,
StaticRoutes: routes}
}
// Equal checks if this instance of SandboxInfo is equal to the passed one
func (s *Info) Equal(o *Info) bool {
if s == o {
return true
}
if o == nil {
return false
}
if !s.Gateway.Equal(o.Gateway) {
return false
}
if !s.GatewayIPv6.Equal(o.GatewayIPv6) {
return false
}
if (s.Interfaces == nil && o.Interfaces != nil) ||
(s.Interfaces != nil && o.Interfaces == nil) ||
(len(s.Interfaces) != len(o.Interfaces)) {
return false
}
// Note: At the moment, the two lists must be in the same order
for i := 0; i < len(s.Interfaces); i++ {
if !s.Interfaces[i].Equal(o.Interfaces[i]) {
return false
}
}
for index := range s.StaticRoutes {
ss := s.StaticRoutes[index]
oo := o.StaticRoutes[index]
if !types.CompareIPNet(ss.Destination, oo.Destination) {
return false
}
if !ss.NextHop.Equal(oo.NextHop) {
return false
}
}
return true
Routes() []*net.IPNet
// Bridge returns true if the interface is a bridge
Bridge() bool
// Master returns the srcname of the master interface for this interface.
Master() string
// Remove an interface from the sandbox by renaming to original name
// and moving it out of the sandbox.
Remove() error
}

View File

@ -40,7 +40,7 @@ func newKey(t *testing.T) (string, error) {
return name, nil
}
func newInfo(t *testing.T) (*Info, error) {
func newInfo(t *testing.T) (Sandbox, error) {
veth := &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{Name: vethName1, TxQLen: 0},
PeerName: vethName2}
@ -50,31 +50,36 @@ func newInfo(t *testing.T) (*Info, error) {
// Store the sandbox side pipe interface
// This is needed for cleanup on DeleteEndpoint()
intf1 := &Interface{}
intf1.SrcName = vethName2
intf1.DstName = sboxIfaceName
intf1 := &nwIface{}
intf1.srcName = vethName2
intf1.dstName = sboxIfaceName
ip4, addr, err := net.ParseCIDR("192.168.1.100/24")
if err != nil {
return nil, err
}
intf1.Address = addr
intf1.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
}
intf1.AddressIPv6 = addrv6
intf1.AddressIPv6.IP = ip6
intf1.addressIPv6 = addrv6
intf1.addressIPv6.IP = ip6
_, route, err := net.ParseCIDR("192.168.2.1/32")
if err != nil {
return nil, err
}
intf1.Routes = []*net.IPNet{route}
intf1.routes = []*net.IPNet{route}
intf2 := &nwIface{}
intf2.srcName = "testbridge"
intf2.dstName = sboxIfaceName
intf2.bridge = true
veth = &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{Name: vethName3, TxQLen: 0},
@ -84,36 +89,21 @@ func newInfo(t *testing.T) (*Info, error) {
return nil, err
}
intf2 := &Interface{}
intf2.SrcName = vethName4
intf2.DstName = sboxIfaceName
intf3 := &nwIface{}
intf3.srcName = vethName4
intf3.dstName = sboxIfaceName
intf3.master = "testbridge"
ip4, addr, err = net.ParseCIDR("192.168.2.100/24")
if err != nil {
return nil, err
}
intf2.Address = addr
intf2.Address.IP = ip4
info := &networkNamespace{iFaces: []*nwIface{intf1, intf2, intf3}}
// 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")
info.gw = net.ParseIP("192.168.1.1")
// sinfo.GatewayIPv6 = net.ParseIP("2001:DB8::1")
sinfo.GatewayIPv6 = net.ParseIP("fe80::1")
info.gwv6 = net.ParseIP("fe80::1")
return sinfo, nil
return info, nil
}
func verifySandbox(t *testing.T, s Sandbox) {
func verifySandbox(t *testing.T, s Sandbox, ifaceSuffixes []string) {
_, ok := s.(*networkNamespace)
if !ok {
t.Fatalf("The sandox interface returned is not of type networkNamespace")
@ -140,16 +130,12 @@ func verifySandbox(t *testing.T, s Sandbox) {
}
defer netns.Set(origns)
_, err = netlink.LinkByName(sboxIfaceName + "0")
if err != nil {
t.Fatalf("Could not find the interface %s inside the sandbox: %v", sboxIfaceName+"0",
err)
}
_, err = netlink.LinkByName(sboxIfaceName + "1")
if err != nil {
t.Fatalf("Could not find the interface %s inside the sandbox: %v", sboxIfaceName+"1",
err)
for _, suffix := range ifaceSuffixes {
_, err = netlink.LinkByName(sboxIfaceName + suffix)
if err != nil {
t.Fatalf("Could not find the interface %s inside the sandbox: %v",
sboxIfaceName+suffix, err)
}
}
}

View File

@ -1,11 +1,12 @@
package sandbox
import (
"net"
"os"
"runtime"
"testing"
"github.com/docker/docker/pkg/reexec"
"github.com/docker/libnetwork/netutils"
)
func TestMain(m *testing.M) {
@ -16,6 +17,8 @@ func TestMain(m *testing.M) {
}
func TestSandboxCreate(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
key, err := newKey(t)
if err != nil {
t.Fatalf("Failed to obtain a key: %v", err)
@ -25,39 +28,50 @@ func TestSandboxCreate(t *testing.T) {
if err != nil {
t.Fatalf("Failed to create a new sandbox: %v", err)
}
runtime.LockOSThread()
if s.Key() != key {
t.Fatalf("s.Key() returned %s. Expected %s", s.Key(), key)
}
info, err := newInfo(t)
tbox, err := newInfo(t)
if err != nil {
t.Fatalf("Failed to generate new sandbox info: %v", err)
}
for _, i := range info.Interfaces {
err = s.AddInterface(i)
for _, i := range tbox.Info().Interfaces() {
err = s.AddInterface(i.SrcName(), i.DstName(),
tbox.InterfaceOptions().Bridge(i.Bridge()),
tbox.InterfaceOptions().Address(i.Address()),
tbox.InterfaceOptions().AddressIPv6(i.AddressIPv6()))
if err != nil {
t.Fatalf("Failed to add interfaces to sandbox: %v", err)
}
runtime.LockOSThread()
}
err = s.SetGateway(info.Gateway)
err = s.SetGateway(tbox.Info().Gateway())
if err != nil {
t.Fatalf("Failed to set gateway to sandbox: %v", err)
}
runtime.LockOSThread()
err = s.SetGatewayIPv6(info.GatewayIPv6)
err = s.SetGatewayIPv6(tbox.Info().GatewayIPv6())
if err != nil {
t.Fatalf("Failed to set ipv6 gateway to sandbox: %v", err)
}
runtime.LockOSThread()
verifySandbox(t, s, []string{"0", "1", "2"})
runtime.LockOSThread()
verifySandbox(t, s)
s.Destroy()
verifyCleanup(t, s, true)
}
func TestSandboxCreateTwice(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
key, err := newKey(t)
if err != nil {
t.Fatalf("Failed to obtain a key: %v", err)
@ -67,6 +81,7 @@ func TestSandboxCreateTwice(t *testing.T) {
if err != nil {
t.Fatalf("Failed to create a new sandbox: %v", err)
}
runtime.LockOSThread()
// Create another sandbox with the same key to see if we handle it
// gracefully.
@ -74,6 +89,7 @@ func TestSandboxCreateTwice(t *testing.T) {
if err != nil {
t.Fatalf("Failed to create a new sandbox: %v", err)
}
runtime.LockOSThread()
s.Destroy()
}
@ -95,6 +111,8 @@ func TestSandboxGC(t *testing.T) {
}
func TestAddRemoveInterface(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
key, err := newKey(t)
if err != nil {
t.Fatalf("Failed to obtain a key: %v", err)
@ -104,128 +122,51 @@ func TestAddRemoveInterface(t *testing.T) {
if err != nil {
t.Fatalf("Failed to create a new sandbox: %v", err)
}
runtime.LockOSThread()
if s.Key() != key {
t.Fatalf("s.Key() returned %s. Expected %s", s.Key(), key)
}
info, err := newInfo(t)
tbox, err := newInfo(t)
if err != nil {
t.Fatalf("Failed to generate new sandbox info: %v", err)
}
for _, i := range info.Interfaces {
err = s.AddInterface(i)
for _, i := range tbox.Info().Interfaces() {
err = s.AddInterface(i.SrcName(), i.DstName(),
tbox.InterfaceOptions().Bridge(i.Bridge()),
tbox.InterfaceOptions().Address(i.Address()),
tbox.InterfaceOptions().AddressIPv6(i.AddressIPv6()))
if err != nil {
t.Fatalf("Failed to add interfaces to sandbox: %v", err)
}
runtime.LockOSThread()
}
interfaces := s.Interfaces()
if !(interfaces[0].Equal(info.Interfaces[0]) && interfaces[1].Equal(info.Interfaces[1])) {
t.Fatalf("Failed to update Sandbox.sinfo.Interfaces in AddInterfaces")
}
verifySandbox(t, s, []string{"0", "1", "2"})
runtime.LockOSThread()
if err := s.RemoveInterface(info.Interfaces[0]); err != nil {
interfaces := s.Info().Interfaces()
if err := interfaces[0].Remove(); err != nil {
t.Fatalf("Failed to remove interfaces from sandbox: %v", err)
}
runtime.LockOSThread()
if !s.Interfaces()[0].Equal(info.Interfaces[1]) {
t.Fatalf("Failed to update the sanbox.sinfo.Interfaces in RemoveInterferce")
}
verifySandbox(t, s, []string{"1", "2"})
runtime.LockOSThread()
if err := s.AddInterface(info.Interfaces[0]); err != nil {
i := tbox.Info().Interfaces()[0]
if err := s.AddInterface(i.SrcName(), i.DstName(),
tbox.InterfaceOptions().Bridge(i.Bridge()),
tbox.InterfaceOptions().Address(i.Address()),
tbox.InterfaceOptions().AddressIPv6(i.AddressIPv6())); err != nil {
t.Fatalf("Failed to add interfaces to sandbox: %v", err)
}
runtime.LockOSThread()
interfaces = s.Interfaces()
if !(interfaces[0].Equal(info.Interfaces[1]) && interfaces[1].Equal(info.Interfaces[0])) {
t.Fatalf("Failed to update Sandbox.sinfo.Interfaces in AddInterfaces")
}
verifySandbox(t, s, []string{"1", "2", "3"})
runtime.LockOSThread()
s.Destroy()
}
func TestInterfaceEqual(t *testing.T) {
list := getInterfaceList()
if !list[0].Equal(list[0]) {
t.Fatalf("Interface.Equal() returned false negative")
}
if list[0].Equal(list[1]) {
t.Fatalf("Interface.Equal() returned false positive")
}
if list[0].Equal(list[1]) != list[1].Equal(list[0]) {
t.Fatalf("Interface.Equal() failed commutative check")
}
}
func TestSandboxInfoEqual(t *testing.T) {
si1 := &Info{Interfaces: getInterfaceList(), Gateway: net.ParseIP("192.168.1.254"), GatewayIPv6: net.ParseIP("2001:2345::abcd:8889")}
si2 := &Info{Interfaces: getInterfaceList(), Gateway: net.ParseIP("172.18.255.254"), GatewayIPv6: net.ParseIP("2001:2345::abcd:8888")}
if !si1.Equal(si1) {
t.Fatalf("Info.Equal() returned false negative")
}
if si1.Equal(si2) {
t.Fatalf("Info.Equal() returned false positive")
}
if si1.Equal(si2) != si2.Equal(si1) {
t.Fatalf("Info.Equal() failed commutative check")
}
}
func TestInterfaceCopy(t *testing.T) {
for _, iface := range getInterfaceList() {
cp := iface.GetCopy()
if !iface.Equal(cp) {
t.Fatalf("Failed to return a copy of Interface")
}
if iface == cp {
t.Fatalf("Failed to return a true copy of Interface")
}
}
}
func TestSandboxInfoCopy(t *testing.T) {
si := Info{Interfaces: getInterfaceList(), Gateway: net.ParseIP("192.168.1.254"), GatewayIPv6: net.ParseIP("2001:2345::abcd:8889")}
cp := si.GetCopy()
if !si.Equal(cp) {
t.Fatalf("Failed to return a copy of Info")
}
if &si == cp {
t.Fatalf("Failed to return a true copy of Info")
}
}
func getInterfaceList() []*Interface {
_, netv4a, _ := net.ParseCIDR("192.168.30.1/24")
_, netv4b, _ := net.ParseCIDR("172.18.255.2/23")
_, netv6a, _ := net.ParseCIDR("2001:2345::abcd:8888/80")
_, netv6b, _ := net.ParseCIDR("2001:2345::abcd:8889/80")
return []*Interface{
&Interface{
SrcName: "veth1234567",
DstName: "eth0",
Address: netv4a,
AddressIPv6: netv6a,
Routes: []*net.IPNet{netv4a, netv6a},
},
&Interface{
SrcName: "veth7654321",
DstName: "eth1",
Address: netv4b,
AddressIPv6: netv6b,
Routes: []*net.IPNet{netv4b, netv6b},
},
}
}

View File

@ -83,17 +83,16 @@ func (s *sandboxData) addEndpoint(ep *endpoint) error {
sb := s.sandbox()
for _, i := range ifaces {
iface := &sandbox.Interface{
SrcName: i.srcName,
DstName: i.dstPrefix,
Address: &i.addr,
Routes: i.routes,
}
var ifaceOptions []sandbox.IfaceOption
ifaceOptions = append(ifaceOptions, sb.InterfaceOptions().Address(&i.addr),
sb.InterfaceOptions().Routes(i.routes))
if i.addrv6.IP.To16() != nil {
iface.AddressIPv6 = &i.addrv6
ifaceOptions = append(ifaceOptions,
sb.InterfaceOptions().AddressIPv6(&i.addrv6))
}
if err := sb.AddInterface(iface); err != nil {
if err := sb.AddInterface(i.srcName, i.dstPrefix, ifaceOptions...); err != nil {
return err
}
}
@ -131,10 +130,10 @@ func (s *sandboxData) rmEndpoint(ep *endpoint) int {
ep.Unlock()
sb := s.sandbox()
for _, i := range sb.Interfaces() {
for _, i := range sb.Info().Interfaces() {
// Only remove the interfaces owned by this endpoint from the sandbox.
if ep.hasInterface(i.SrcName) {
if err := sb.RemoveInterface(i); err != nil {
if ep.hasInterface(i.SrcName()) {
if err := i.Remove(); err != nil {
logrus.Debugf("Remove interface failed: %v", err)
}
}

View File

@ -1,11 +1,12 @@
package types
import (
"flag"
"testing"
_ "github.com/docker/libnetwork/netutils"
)
var runningInContainer = flag.Bool("incontainer", false, "Indicates if the test is running in a container")
func TestErrorConstructors(t *testing.T) {
var err error