diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index b144e78a6c..dde595ade1 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -14,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" ) @@ -70,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 @@ -802,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 } @@ -882,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 } @@ -897,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) } @@ -981,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 } @@ -1059,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() @@ -1092,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() diff --git a/libnetwork/drivers/bridge/port_mapping.go b/libnetwork/drivers/bridge/port_mapping.go index 8f377954b8..b102132190 100644 --- a/libnetwork/drivers/bridge/port_mapping.go +++ b/libnetwork/drivers/bridge/port_mapping.go @@ -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) { diff --git a/libnetwork/sandbox/configure_linux.go b/libnetwork/sandbox/configure_linux.go deleted file mode 100644 index 4022170820..0000000000 --- a/libnetwork/sandbox/configure_linux.go +++ /dev/null @@ -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 -} diff --git a/libnetwork/sandbox/interface_linux.go b/libnetwork/sandbox/interface_linux.go new file mode 100644 index 0000000000..09339853fd --- /dev/null +++ b/libnetwork/sandbox/interface_linux.go @@ -0,0 +1,284 @@ +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 + address *net.IPNet + addressIPv6 *net.IPNet + routes []*net.IPNet + 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...) + + 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 { + // 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 { + // 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 +} diff --git a/libnetwork/sandbox/namespace_linux.go b/libnetwork/sandbox/namespace_linux.go index 3d8a98ccf6..1b9b7fae6d 100644 --- a/libnetwork/sandbox/namespace_linux.go +++ b/libnetwork/sandbox/namespace_linux.go @@ -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 { diff --git a/libnetwork/sandbox/options_linux.go b/libnetwork/sandbox/options_linux.go new file mode 100644 index 0000000000..40199e0c38 --- /dev/null +++ b/libnetwork/sandbox/options_linux.go @@ -0,0 +1,29 @@ +package sandbox + +import "net" + +func (i *nwIface) processInterfaceOptions(options ...IfaceOption) { + for _, opt := range options { + if opt != nil { + opt(i) + } + } +} + +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 + } +} diff --git a/libnetwork/sandbox/route_linux.go b/libnetwork/sandbox/route_linux.go new file mode 100644 index 0000000000..832651488c --- /dev/null +++ b/libnetwork/sandbox/route_linux.go @@ -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 +} diff --git a/libnetwork/sandbox/sandbox.go b/libnetwork/sandbox/sandbox.go index e52fba031c..92bf4cc6fc 100644 --- a/libnetwork/sandbox/sandbox.go +++ b/libnetwork/sandbox/sandbox.go @@ -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,47 @@ 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 { + // 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 + + // 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 +87,26 @@ 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 + + // Remove an interface from the sandbox by renaming to original name + // and moving it out of the sandbox. + Remove() error } diff --git a/libnetwork/sandbox/sandbox_linux_test.go b/libnetwork/sandbox/sandbox_linux_test.go index 67175b11e3..e47ea78262 100644 --- a/libnetwork/sandbox/sandbox_linux_test.go +++ b/libnetwork/sandbox/sandbox_linux_test.go @@ -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,31 @@ 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} veth = &netlink.Veth{ LinkAttrs: netlink.LinkAttrs{Name: vethName3, TxQLen: 0}, @@ -84,16 +84,16 @@ func newInfo(t *testing.T) (*Info, error) { return nil, err } - intf2 := &Interface{} - intf2.SrcName = vethName4 - intf2.DstName = sboxIfaceName + intf2 := &nwIface{} + intf2.srcName = vethName4 + intf2.dstName = sboxIfaceName ip4, addr, err = net.ParseCIDR("192.168.2.100/24") if err != nil { return nil, err } - intf2.Address = addr - intf2.Address.IP = ip4 + intf2.address = addr + intf2.address.IP = ip4 // ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48") ip6, addrv6, err = net.ParseCIDR("fe80::3/64") @@ -101,19 +101,19 @@ func newInfo(t *testing.T) (*Info, error) { if err != nil { return nil, err } - intf2.AddressIPv6 = addrv6 - intf2.AddressIPv6.IP = ip6 + intf2.addressIPv6 = addrv6 + intf2.addressIPv6.IP = ip6 - sinfo := &Info{Interfaces: []*Interface{intf1, intf2}} + info := &networkNamespace{iFaces: []*nwIface{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 +140,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) + } } } diff --git a/libnetwork/sandbox/sandbox_test.go b/libnetwork/sandbox/sandbox_test.go index ca0d6ba1b3..19e87ca863 100644 --- a/libnetwork/sandbox/sandbox_test.go +++ b/libnetwork/sandbox/sandbox_test.go @@ -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,49 @@ 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().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"}) + 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 +80,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 +88,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 +110,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 +121,49 @@ 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().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"}) + 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"}) + 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().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"}) + 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}, - }, - } -} diff --git a/libnetwork/sandboxdata.go b/libnetwork/sandboxdata.go index 947fea3863..58e69866a1 100644 --- a/libnetwork/sandboxdata.go +++ b/libnetwork/sandboxdata.go @@ -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) } }