Merge pull request #257 from mrjana/overlay
Refactor sandbox code to use interfaces and add bridge support to sandbox
This commit is contained in:
commit
d861b7ec70
|
@ -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{}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue