diff --git a/pkg/sentry/inet/inet.go b/pkg/sentry/inet/inet.go index 8b4ef8848e..7b6af3c9e9 100644 --- a/pkg/sentry/inet/inet.go +++ b/pkg/sentry/inet/inet.go @@ -100,14 +100,6 @@ type Stack interface { // Restore restarts the network stack after restore. Restore() - // ReplaceConfig replaces the new network stack configuration to the - // loaded or saved network stack after restore. - // TODO(b/379115439): This method is a workaround to update netstack config - // during restore. It should be removed after a new method is added to - // extract the complete config from the spec and update it in the loaded - // stack during restore. - ReplaceConfig(st Stack) - // Destroy the network stack. Destroy() diff --git a/pkg/sentry/inet/test_stack.go b/pkg/sentry/inet/test_stack.go index 26d3f61651..7803d0f42e 100644 --- a/pkg/sentry/inet/test_stack.go +++ b/pkg/sentry/inet/test_stack.go @@ -175,9 +175,6 @@ func (s *TestStack) Pause() {} // Restore implements Stack. func (s *TestStack) Restore() {} -// ReplaceConfig implements Stack. -func (s *TestStack) ReplaceConfig(_ Stack) {} - // Resume implements Stack. func (s *TestStack) Resume() {} diff --git a/pkg/sentry/kernel/kernel.go b/pkg/sentry/kernel/kernel.go index d9e0a9c421..24aa551592 100644 --- a/pkg/sentry/kernel/kernel.go +++ b/pkg/sentry/kernel/kernel.go @@ -836,16 +836,6 @@ func (k *Kernel) LoadFrom(ctx context.Context, r, pagesMetadata io.Reader, pages if saveRestoreNet { log.Infof("netstack save restore is enabled") - s := k.rootNetworkNamespace.Stack() - if s == nil { - panic("inet.Stack cannot be nil when netstack s/r is enabled") - } - if net != nil { - s.ReplaceConfig(net) - } - s.Restore() - } else if net != nil { - net.Restore() } if err := k.vfs.CompleteRestore(ctx, vfsOpts); err != nil { diff --git a/pkg/sentry/socket/hostinet/stack.go b/pkg/sentry/socket/hostinet/stack.go index 4d1facb0af..cc9f35f88e 100644 --- a/pkg/sentry/socket/hostinet/stack.go +++ b/pkg/sentry/socket/hostinet/stack.go @@ -398,9 +398,6 @@ func (*Stack) Pause() {} // Restore implements inet.Stack.Restore. func (*Stack) Restore() {} -// ReplaceConfig implements inet.Stack.ReplaceConfig. -func (s *Stack) ReplaceConfig(_ inet.Stack) {} - // Resume implements inet.Stack.Resume. func (*Stack) Resume() {} diff --git a/pkg/sentry/socket/netstack/stack.go b/pkg/sentry/socket/netstack/stack.go index fc21771bd3..3a780c3d99 100644 --- a/pkg/sentry/socket/netstack/stack.go +++ b/pkg/sentry/socket/netstack/stack.go @@ -23,6 +23,7 @@ import ( "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/refs" "gvisor.dev/gvisor/pkg/sentry/inet" + "gvisor.dev/gvisor/pkg/sentry/socket/netfilter" "gvisor.dev/gvisor/pkg/sentry/socket/netlink/nlmsg" "gvisor.dev/gvisor/pkg/syserr" "gvisor.dev/gvisor/pkg/tcpip" @@ -922,15 +923,8 @@ func (s *Stack) Pause() { // Restore implements inet.Stack.Restore. func (s *Stack) Restore() { - s.Stack.Restore() -} - -// ReplaceConfig implements inet.Stack.ReplaceConfig. -func (s *Stack) ReplaceConfig(st inet.Stack) { - if _, ok := st.(*Stack); !ok { - panic("netstack.Stack cannot be nil when netstack s/r is enabled") - } - s.Stack.ReplaceConfig(st.(*Stack).Stack) + tables := netfilter.DefaultLinuxTables + s.Stack.Restore(tables) } // Resume implements inet.Stack.Resume. diff --git a/pkg/tcpip/stack/save_restore.go b/pkg/tcpip/stack/save_restore.go index 838cf5f4fd..58961ba027 100644 --- a/pkg/tcpip/stack/save_restore.go +++ b/pkg/tcpip/stack/save_restore.go @@ -20,10 +20,14 @@ import ( "time" cryptorand "gvisor.dev/gvisor/pkg/rand" + "gvisor.dev/gvisor/pkg/tcpip" ) // afterLoad is invoked by stateify. func (s *Stack) afterLoad(context.Context) { s.insecureRNG = rand.New(rand.NewSource(time.Now().UnixNano())) s.secureRNG = cryptorand.RNGFrom(cryptorand.Reader) + s.mu.Lock() + s.nics = make(map[tcpip.NICID]*nic) + s.mu.Unlock() } diff --git a/pkg/tcpip/stack/stack.go b/pkg/tcpip/stack/stack.go index 472b6d4b03..67926293f0 100644 --- a/pkg/tcpip/stack/stack.go +++ b/pkg/tcpip/stack/stack.go @@ -1998,13 +1998,15 @@ func (s *Stack) ReplaceConfig(st *Stack) { // Restore restarts the stack after a restore. This must be called after the // entire system has been restored. -func (s *Stack) Restore() { +func (s *Stack) Restore(fn func(clock tcpip.Clock, rand *rand.Rand) *IPTables) { // RestoredEndpoint.Restore() may call other methods on s, so we can't hold // s.mu while restoring the endpoints. s.mu.Lock() eps := s.restoredEndpoints s.restoredEndpoints = nil saveRestoreEnabled := s.saveRestoreEnabled + s.tables = fn(s.clock, s.insecureRNG) + s.icmpRateLimiter = NewICMPRateLimiter(s.clock) s.mu.Unlock() for _, e := range eps { e.Restore(s) diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go index 217280b1e0..809cd1ef69 100644 --- a/runsc/boot/controller.go +++ b/runsc/boot/controller.go @@ -120,6 +120,9 @@ const ( // ContMgrContainerRuntimeState returns the runtime state of a container. ContMgrContainerRuntimeState = "containerManager.ContainerRuntimeState" + + // ContMgrStoreNetworkConfig stores the network configuration in the loader. + ContMgrStoreNetworkConfig = "containerManager.StoreNetworkConfig" ) const ( @@ -131,6 +134,9 @@ const ( // DebugStacks collects sandbox stacks for debugging. DebugStacks = "debug.Stacks" + + // NetworkSetupNetwork sets up network. + NetworkSetupNetwork = "Network.SetupNetwork" ) // Profiling related commands (see pprof.go for more details). @@ -943,3 +949,9 @@ func (cm *containerManager) ContainerRuntimeState(cid *string, state *ContainerR *state = cm.l.containerRuntimeState(*cid) return nil } + +// StoreNetworkConfig stores the network configuration. +func (cm *containerManager) StoreNetworkConfig(netConf *NetworkConfig, _ *struct{}) error { + cm.l.netConf = netConf + return nil +} diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 4ae3b787e5..1a58d1a23f 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -255,6 +255,9 @@ type Loader struct { // saveRestoreNet indicates if the saved network stack should be used // during restore. saveRestoreNet bool + + // netConf contains the network configuration required during restore. + netConf *NetworkConfig } // execID uniquely identifies a sentry process that is executed in a container. diff --git a/runsc/boot/network.go b/runsc/boot/network.go index 19bf6d6f71..4728d1084f 100644 --- a/runsc/boot/network.go +++ b/runsc/boot/network.go @@ -605,3 +605,27 @@ func ipMaskToAddressMask(ipMask net.IPMask) tcpip.AddressMask { addr := ipToAddress(net.IP(ipMask)) return tcpip.MaskFromBytes(addr.AsSlice()) } + +// NetworkConfig contains network configuration. +type NetworkConfig struct { + Args *CreateLinksAndRoutesArgs + InitArgs *InitPluginStackArgs + Network config.NetworkType +} + +// SetupNetwork sets up the network during start and restore. +func (n *Network) SetupNetwork(netConf *NetworkConfig, _ *struct{}) error { + switch netConf.Network { + case config.NetworkNone, config.NetworkSandbox: + if err := n.CreateLinksAndRoutes(netConf.Args, nil); err != nil { + return err + } + case config.NetworkPlugin: + if err := n.InitPluginStack(netConf.InitArgs, nil); err != nil { + return err + } + default: + return fmt.Errorf("invalid network type: %v", netConf.Network) + } + return nil +} diff --git a/runsc/boot/restore.go b/runsc/boot/restore.go index 03c0147876..34797805ef 100644 --- a/runsc/boot/restore.go +++ b/runsc/boot/restore.go @@ -143,6 +143,22 @@ func createNetworkStackForRestore(l *Loader) (*stack.Stack, inet.Stack) { return nil, hostinet.NewStack() } +// IsXDP returns true if the XDP mode has to be enabled in network. +func IsXDP(conf *config.Config) bool { + if conf.Network != config.NetworkSandbox { + return false + } + switch conf.XDP.Mode { + case config.XDPModeOff: + case config.XDPModeNS: + case config.XDPModeRedirect, config.XDPModeTunnel: + return true + default: + panic("invalid XDP mode configured") + } + return false +} + func (r *restorer) restore(l *Loader) error { log.Infof("Starting to restore %d containers", len(r.containers)) @@ -311,6 +327,44 @@ func (r *restorer) restore(l *Loader) error { // Release `l.mu` before calling into callbacks. cu.Clean() + if IsXDP(l.root.conf) { + curNetwork := l.k.RootNetworkNamespace().Stack() + eps, ok := curNetwork.(*netstack.Stack) + if !ok { + return fmt.Errorf("invalid network config with XDP mode") + + } + if oldStack == nil { + return fmt.Errorf("invalid network config with XDP mode") + } + // TODO(b/340617793): Configure routes and devices in the loaded stack + // similar to non-XDP and remove ReplaceConfig. + eps.Stack.ReplaceConfig(oldStack) + l.k.RootNetworkNamespace().Stack().Restore() + } else if l.saveRestoreNet { + curNetwork := l.k.RootNetworkNamespace().Stack() + if eps, ok := curNetwork.(*netstack.Stack); ok { + if oldStack != nil { + oldStack.Destroy() + } + n := &Network{ + Stack: eps.Stack, + Kernel: l.k, + } + if err := n.SetupNetwork(l.netConf, nil); err != nil { + return fmt.Errorf("SetupNetwork failed with error: %v", err) + } + l.k.RootNetworkNamespace().Stack().Restore() + } else { + // Restore the network stack with a new hostinet stack. + // Save/Restore is not supported for hostinet. + l.k.RootNetworkNamespace().RestoreRootStack(hostinet.NewStack()) + } + } else { + // TODO(b/340617793): Delete when netstack s/r is enabled by default. + l.k.RootNetworkNamespace().Stack().Restore() + } + // r.restoreDone() signals and waits for the sandbox to start. if err := r.restoreDone(); err != nil { return fmt.Errorf("restorer.restoreDone callback failed: %w", err) diff --git a/runsc/sandbox/network.go b/runsc/sandbox/network.go index 7d4a3b5c67..d726ea80bc 100644 --- a/runsc/sandbox/network.go +++ b/runsc/sandbox/network.go @@ -55,41 +55,102 @@ import ( func setupNetwork(conn *urpc.Client, pid int, conf *config.Config) error { log.Infof("Setting up network") + if conf.Network == config.NetworkHost { + // Nothing to do here. + return nil + } + + netConf, err := getNetworkConfig(conn, pid, conf) + if err != nil { + return fmt.Errorf("getNetworkConfig failed with error: %v", err) + } + + if err := conn.Call(boot.NetworkSetupNetwork, netConf, nil); err != nil { + return fmt.Errorf("SetupNetwork failed with error: %v", err) + } + return nil +} + +func checkAndConfigureXDP(conn *urpc.Client, pid int, conf *config.Config) (bool, error) { + nsPath := filepath.Join("/proc", strconv.Itoa(pid), "ns/net") + switch conf.XDP.Mode { + case config.XDPModeOff: + case config.XDPModeNS: + case config.XDPModeRedirect: + if err := createRedirectInterfacesAndRoutes(conn, conf); err != nil { + return true, fmt.Errorf("failed to create XDP redirect interface: %w", err) + } + return true, nil + case config.XDPModeTunnel: + if err := createXDPTunnel(conn, nsPath, conf); err != nil { + return true, fmt.Errorf("failed to create XDP tunnel: %w", err) + } + return true, nil + default: + return true, fmt.Errorf("unknown XDP mode: %v", conf.XDP.Mode) + } + return false, nil +} + +func getNetworkConfig(conn *urpc.Client, pid int, conf *config.Config) (*boot.NetworkConfig, error) { switch conf.Network { case config.NetworkNone: - log.Infof("Network is disabled, create loopback interface only") - if err := createDefaultLoopbackInterface(conf, conn); err != nil { - return fmt.Errorf("creating default loopback interface: %v", err) - } + return getLoopbackNetworkConfig(conf) + case config.NetworkPlugin: + return getPluginNetworkConfig(pid, conf) case config.NetworkSandbox: - // Build the path to the net namespace of the sandbox process. - // This is what we will copy. - nsPath := filepath.Join("/proc", strconv.Itoa(pid), "ns/net") - if err := createInterfacesAndRoutesFromNS(conn, nsPath, conf); err != nil { - return fmt.Errorf("creating interfaces from net namespace %q: %v", nsPath, err) + isXDP, err := checkAndConfigureXDP(conn, pid, conf) + if err != nil { + return nil, fmt.Errorf("setup XDP network failed with error: %v", err) } - case config.NetworkHost: - // Nothing to do here. - case config.NetworkPlugin: - if err := initPluginStack(conn, pid, conf); err != nil { - return fmt.Errorf("failed to initialize external stack, error: %v", err) + if isXDP { + return nil, nil } + return getSandboxNetworkConfig(pid, conf) + case config.NetworkHost: + return nil, nil default: - return fmt.Errorf("invalid network type: %v", conf.Network) + return nil, fmt.Errorf("invalid network type: %v", conf.Network) } - return nil } -func createDefaultLoopbackInterface(conf *config.Config, conn *urpc.Client) error { +func getLoopbackNetworkConfig(conf *config.Config) (*boot.NetworkConfig, error) { link := boot.DefaultLoopbackLink link.GVisorGRO = conf.GVisorGRO - if err := conn.Call(boot.NetworkCreateLinksAndRoutes, &boot.CreateLinksAndRoutesArgs{ + args := &boot.CreateLinksAndRoutesArgs{ LoopbackLinks: []boot.LoopbackLink{link}, DisconnectOk: conf.NetDisconnectOk, - }, nil); err != nil { - return fmt.Errorf("creating loopback link and routes: %v", err) } - return nil + netConf := &boot.NetworkConfig{ + Args: args, + Network: config.NetworkNone, + } + return netConf, nil +} + +func getPluginNetworkConfig(pid int, conf *config.Config) (*boot.NetworkConfig, error) { + pluginStack := plugin.GetPluginStack() + if pluginStack == nil { + return nil, fmt.Errorf("plugin stack is not registered") + } + + initStr, fds, err := pluginStack.PreInit(&plugin.PreInitStackArgs{Pid: pid}) + if err != nil { + return nil, fmt.Errorf("plugin stack PreInit failed: %v", err) + } + args := &boot.InitPluginStackArgs{ + InitStr: initStr, + } + for _, fd := range fds { + args.FilePayload.Files = append(args.FilePayload.Files, os.NewFile(uintptr(fd), "")) + } + + log.Debugf("Initializing plugin network stack, config: %+v", args) + netConf := &boot.NetworkConfig{ + InitArgs: args, + Network: config.NetworkPlugin, + } + return netConf, nil } func joinNetNS(nsPath string) (func(), error) { @@ -122,50 +183,46 @@ func isRootNetNS() (bool, error) { } } +func getSandboxNetworkConfig(pid int, conf *config.Config) (*boot.NetworkConfig, error) { + nsPath := filepath.Join("/proc", strconv.Itoa(pid), "ns/net") + args, err := createInterfacesAndRoutesFromNS(nsPath, conf) + if err != nil { + return nil, err + } + netConf := &boot.NetworkConfig{ + Args: args, + Network: config.NetworkSandbox, + } + return netConf, nil +} + // createInterfacesAndRoutesFromNS scrapes the interface and routes from the // net namespace with the given path, creates them in the sandbox, and removes // them from the host. -func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *config.Config) error { - switch conf.XDP.Mode { - case config.XDPModeOff: - case config.XDPModeNS: - case config.XDPModeRedirect: - if err := createRedirectInterfacesAndRoutes(conn, conf); err != nil { - return fmt.Errorf("failed to create XDP redirect interface: %w", err) - } - return nil - case config.XDPModeTunnel: - if err := createXDPTunnel(conn, nsPath, conf); err != nil { - return fmt.Errorf("failed to create XDP tunnel: %w", err) - } - return nil - default: - return fmt.Errorf("unknown XDP mode: %v", conf.XDP.Mode) - } - +func createInterfacesAndRoutesFromNS(nsPath string, conf *config.Config) (*boot.CreateLinksAndRoutesArgs, error) { // Join the network namespace that we will be copying. restore, err := joinNetNS(nsPath) if err != nil { - return err + return nil, err } defer restore() // Get all interfaces in the namespace. ifaces, err := net.Interfaces() if err != nil { - return fmt.Errorf("querying interfaces: %w", err) + return nil, fmt.Errorf("querying interfaces: %w", err) } isRoot, err := isRootNetNS() if err != nil { - return err + return nil, err } if isRoot { - return fmt.Errorf("cannot run with network enabled in root network namespace") + return nil, fmt.Errorf("cannot run with network enabled in root network namespace") } // Collect addresses and routes from the interfaces. - args := boot.CreateLinksAndRoutesArgs{ + args := &boot.CreateLinksAndRoutesArgs{ DisconnectOk: conf.NetDisconnectOk, } for _, iface := range ifaces { @@ -176,14 +233,14 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con allAddrs, err := iface.Addrs() if err != nil { - return fmt.Errorf("fetching interface addresses for %q: %w", iface.Name, err) + return nil, fmt.Errorf("fetching interface addresses for %q: %w", iface.Name, err) } // We build our own loopback device. if iface.Flags&net.FlagLoopback != 0 { link, err := loopbackLink(conf, iface, allAddrs) if err != nil { - return fmt.Errorf("getting loopback link for iface %q: %w", iface.Name, err) + return nil, fmt.Errorf("getting loopback link for iface %q: %w", iface.Name, err) } args.LoopbackLinks = append(args.LoopbackLinks, link) continue @@ -193,7 +250,7 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con for _, ifaddr := range allAddrs { ipNet, ok := ifaddr.(*net.IPNet) if !ok { - return fmt.Errorf("address is not IPNet: %+v", ifaddr) + return nil, fmt.Errorf("address is not IPNet: %+v", ifaddr) } ipAddrs = append(ipAddrs, ipNet) } @@ -205,7 +262,7 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con // Collect data from the ARP table. dump, err := netlink.NeighList(iface.Index, 0) if err != nil { - return fmt.Errorf("fetching ARP table for %q: %w", iface.Name, err) + return nil, fmt.Errorf("fetching ARP table for %q: %w", iface.Name, err) } var neighbors []boot.Neighbor @@ -223,11 +280,11 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con // will remove the routes as well. routes, defv4, defv6, err := routesForIface(iface) if err != nil { - return fmt.Errorf("getting routes for interface %q: %v", iface.Name, err) + return nil, fmt.Errorf("getting routes for interface %q: %v", iface.Name, err) } if defv4 != nil { if !args.Defaultv4Gateway.Route.Empty() { - return fmt.Errorf("more than one default route found, interface: %v, route: %v, default route: %+v", iface.Name, defv4, args.Defaultv4Gateway) + return nil, fmt.Errorf("more than one default route found, interface: %v, route: %v, default route: %+v", iface.Name, defv4, args.Defaultv4Gateway) } args.Defaultv4Gateway.Route = *defv4 args.Defaultv4Gateway.Name = iface.Name @@ -235,7 +292,7 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con if defv6 != nil { if !args.Defaultv6Gateway.Route.Empty() { - return fmt.Errorf("more than one default route found, interface: %v, route: %v, default route: %+v", iface.Name, defv6, args.Defaultv6Gateway) + return nil, fmt.Errorf("more than one default route found, interface: %v, route: %v, default route: %+v", iface.Name, defv6, args.Defaultv6Gateway) } args.Defaultv6Gateway.Route = *defv6 args.Defaultv6Gateway.Name = iface.Name @@ -244,7 +301,7 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con // Get the link for the interface. ifaceLink, err := netlink.LinkByName(iface.Name) if err != nil { - return fmt.Errorf("getting link for interface %q: %w", iface.Name, err) + return nil, fmt.Errorf("getting link for interface %q: %w", iface.Name, err) } linkAddress := ifaceLink.Attrs().HardwareAddr @@ -260,18 +317,18 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con // If we encounter an error while deleting the ip, // verify the ip is still present on the interface. if present, err := isAddressOnInterface(iface.Name, addr); err != nil { - return fmt.Errorf("checking if address %v is on interface %q: %w", addr, iface.Name, err) + return nil, fmt.Errorf("checking if address %v is on interface %q: %w", addr, iface.Name, err) } else if !present { continue } - return fmt.Errorf("removing address %v from device %q: %w", addr, iface.Name, err) + return nil, fmt.Errorf("removing address %v from device %q: %w", addr, iface.Name, err) } } if conf.XDP.Mode == config.XDPModeNS { xdpSockFDs, err := createSocketXDP(iface) if err != nil { - return fmt.Errorf("failed to create XDP socket: %v", err) + return nil, fmt.Errorf("failed to create XDP socket: %v", err) } args.FilePayload.Files = append(args.FilePayload.Files, xdpSockFDs...) args.XDPLinks = append(args.XDPLinks, boot.XDPLink{ @@ -308,13 +365,13 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con log.Debugf("Creating Channel %d", i) socketEntry, err := createSocket(iface, ifaceLink, conf.HostGSO) if err != nil { - return fmt.Errorf("failed to createSocket for %s : %w", iface.Name, err) + return nil, fmt.Errorf("failed to createSocket for %s : %w", iface.Name, err) } if i == 0 { link.GSOMaxSize = socketEntry.gsoMaxSize } else { if link.GSOMaxSize != socketEntry.gsoMaxSize { - return fmt.Errorf("inconsistent gsoMaxSize %d and %d when creating multiple channels for same interface: %s", + return nil, fmt.Errorf("inconsistent gsoMaxSize %d and %d when creating multiple channels for same interface: %s", link.GSOMaxSize, socketEntry.gsoMaxSize, iface.Name) } } @@ -332,39 +389,12 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con } } - if err := pcapAndNAT(&args, conf); err != nil { - return err + if err := pcapAndNAT(args, conf); err != nil { + return nil, err } log.Debugf("Setting up network, config: %+v", args) - if err := conn.Call(boot.NetworkCreateLinksAndRoutes, &args, nil); err != nil { - return fmt.Errorf("creating links and routes: %w", err) - } - return nil -} - -func initPluginStack(conn *urpc.Client, pid int, conf *config.Config) error { - pluginStack := plugin.GetPluginStack() - if pluginStack == nil { - return fmt.Errorf("plugin stack is not registered") - } - - initStr, fds, err := pluginStack.PreInit(&plugin.PreInitStackArgs{Pid: pid}) - if err != nil { - return fmt.Errorf("plugin stack PreInit failed: %v", err) - } - var args boot.InitPluginStackArgs - args.InitStr = initStr - for _, fd := range fds { - args.FilePayload.Files = append(args.FilePayload.Files, os.NewFile(uintptr(fd), "")) - } - - log.Debugf("Initializing plugin network stack, config: %+v", args) - if err := conn.Call(boot.NetworkInitPluginStack, &args, nil); err != nil { - return fmt.Errorf("error initializing plugin netstack: %v", err) - } - - return nil + return args, nil } // isAddressOnInterface checks if an address is on an interface diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 4695003a53..37c9dd553f 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -528,9 +528,29 @@ func (s *Sandbox) Restore(conf *config.Config, cid string, imagePath string, dir } defer conn.Close() - // Configure the network. - if err := setupNetwork(conn, s.Pid.load(), conf); err != nil { - return fmt.Errorf("setting up network: %v", err) + pid := s.Pid.load() + if !conf.TestOnlySaveRestoreNetstack || boot.IsXDP(conf) { + // Configure the network. Delete this when netstack s/r is + // enabled by default. + if err := setupNetwork(conn, pid, conf); err != nil { + return fmt.Errorf("setting up network: %w", err) + } + } else { + // When netstack s/r is enabled, no need to configure the + // network. Store the network configuration from the spec in + // the loader and netstack will be restored after load. + netConf, err := getNetworkConfig(conn, s.Pid.load(), conf) + if err != nil { + return fmt.Errorf("getNetworkConfig failed with error: %v", err) + } + + // netConf will be nil for host network. + if netConf != nil { + if err := conn.Call(boot.ContMgrStoreNetworkConfig, netConf, nil); err != nil { + return fmt.Errorf("storing network config: %w", err) + } + } + } // Restore the container and start the root container.