diff --git a/container_linux.go b/container_linux.go index e35f7021..84134616 100644 --- a/container_linux.go +++ b/container_linux.go @@ -417,6 +417,18 @@ func (c *linuxContainer) Restore(process *Process, imagePath string) error { req.Opts.ExtMnt = append(req.Opts.ExtMnt, extMnt) } } + for _, iface := range c.config.Networks { + switch iface.Type { + case "veth": + veth := new(criurpc.CriuVethPair) + veth.IfOut = proto.String(iface.HostInterfaceName) + veth.IfIn = proto.String(iface.Name) + req.Opts.Veths = append(req.Opts.Veths, veth) + break + case "loopback": + break + } + } // Pipes that were previously set up for std{in,out,err} // were removed after checkpoint. Use the new ones. var i int32 @@ -555,6 +567,34 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, imageP return nil } +// block any external network activity +func lockNetwork(config *configs.Config) error { + for _, config := range config.Networks { + strategy, err := getStrategy(config.Type) + if err != nil { + return err + } + + if err := strategy.detach(config); err != nil { + return err + } + } + return nil +} + +func unlockNetwork(config *configs.Config) error { + for _, config := range config.Networks { + strategy, err := getStrategy(config.Type) + if err != nil { + return err + } + if err = strategy.attach(config); err != nil { + return err + } + } + return nil +} + func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Process, imagePath string) error { notify := resp.GetNotify() if notify == nil { @@ -570,6 +610,18 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc f.Close() break + case notify.GetScript() == "network-unlock": + if err := unlockNetwork(c.config); err != nil { + return err + } + break + + case notify.GetScript() == "network-lock": + if err := lockNetwork(c.config); err != nil { + return err + } + break + case notify.GetScript() == "post-restore": pid := notify.GetPid() r, err := newRestoredProcess(int(pid)) diff --git a/netlink/netlink_linux.go b/netlink/netlink_linux.go index 267885dc..c81c269e 100644 --- a/netlink/netlink_linux.go +++ b/netlink/netlink_linux.go @@ -27,6 +27,7 @@ const ( SIOC_BRADDBR = 0x89a0 SIOC_BRDELBR = 0x89a1 SIOC_BRADDIF = 0x89a2 + SIOC_BRDELIF = 0x89a3 ) const ( @@ -1191,9 +1192,7 @@ func DeleteBridge(name string) error { return nil } -// Add a slave to abridge device. This is more backward-compatible than -// netlink.NetworkSetMaster and works on RHEL 6. -func AddToBridge(iface, master *net.Interface) error { +func ifIoctBridge(iface, master *net.Interface, op uintptr) error { if len(master.Name) >= IFNAMSIZ { return fmt.Errorf("Interface name %s too long", master.Name) } @@ -1208,13 +1207,25 @@ func AddToBridge(iface, master *net.Interface) error { copy(ifr.IfrnName[:len(ifr.IfrnName)-1], master.Name) ifr.IfruIndex = int32(iface.Index) - if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), SIOC_BRADDIF, uintptr(unsafe.Pointer(&ifr))); err != 0 { + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), op, uintptr(unsafe.Pointer(&ifr))); err != 0 { return err } return nil } +// Add a slave to a bridge device. This is more backward-compatible than +// netlink.NetworkSetMaster and works on RHEL 6. +func AddToBridge(iface, master *net.Interface) error { + return ifIoctBridge(iface, master, SIOC_BRADDIF) +} + +// Detach a slave from a bridge device. This is more backward-compatible than +// netlink.NetworkSetMaster and works on RHEL 6. +func DelFromBridge(iface, master *net.Interface) error { + return ifIoctBridge(iface, master, SIOC_BRDELIF) +} + func randMacAddr() string { hw := make(net.HardwareAddr, 6) for i := 0; i < 6; i++ { diff --git a/network_linux.go b/network_linux.go index 46c606a2..b88009ff 100644 --- a/network_linux.go +++ b/network_linux.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" + "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/netlink" "github.com/docker/libcontainer/utils" ) @@ -24,6 +25,8 @@ var strategies = map[string]networkStrategy{ type networkStrategy interface { create(*network, int) error initialize(*network) error + detach(*configs.Network) error + attach(*configs.Network) error } // getStrategy returns the specific network strategy for the @@ -97,32 +100,39 @@ func (l *loopback) initialize(config *network) error { return netlink.NetworkLinkUp(iface) } +func (l *loopback) attach(n *configs.Network) (err error) { + return nil +} + +func (l *loopback) detach(n *configs.Network) (err error) { + return nil +} + // veth is a network strategy that uses a bridge and creates // a veth pair, one that is attached to the bridge on the host and the other // is placed inside the container's namespace type veth struct { } -func (v *veth) create(n *network, nspid int) (err error) { - tmpName, err := v.generateTempPeerName() - if err != nil { - return err - } - n.TempVethPeerName = tmpName - defer func() { - if err != nil { - netlink.NetworkLinkDel(n.HostInterfaceName) - netlink.NetworkLinkDel(n.TempVethPeerName) - } - }() - if n.Bridge == "" { - return fmt.Errorf("bridge is not specified") - } +func (v *veth) detach(n *configs.Network) (err error) { bridge, err := net.InterfaceByName(n.Bridge) if err != nil { return err } - if err := netlink.NetworkCreateVethPair(n.HostInterfaceName, n.TempVethPeerName, n.TxQueueLen); err != nil { + host, err := net.InterfaceByName(n.HostInterfaceName) + if err != nil { + return err + } + if err := netlink.DelFromBridge(host, bridge); err != nil { + return err + } + return nil +} + +// attach a container network interface to an external network +func (v *veth) attach(n *configs.Network) (err error) { + bridge, err := net.InterfaceByName(n.Bridge) + if err != nil { return err } host, err := net.InterfaceByName(n.HostInterfaceName) @@ -143,6 +153,31 @@ func (v *veth) create(n *network, nspid int) (err error) { if err := netlink.NetworkLinkUp(host); err != nil { return err } + + return nil +} + +func (v *veth) create(n *network, nspid int) (err error) { + tmpName, err := v.generateTempPeerName() + if err != nil { + return err + } + n.TempVethPeerName = tmpName + defer func() { + if err != nil { + netlink.NetworkLinkDel(n.HostInterfaceName) + netlink.NetworkLinkDel(n.TempVethPeerName) + } + }() + if n.Bridge == "" { + return fmt.Errorf("bridge is not specified") + } + if err := netlink.NetworkCreateVethPair(n.HostInterfaceName, n.TempVethPeerName, n.TxQueueLen); err != nil { + return err + } + if err := v.attach(&n.Network); err != nil { + return err + } child, err := net.InterfaceByName(n.TempVethPeerName) if err != nil { return err