From 221c4d5f0692f84b3c9de7cf820d4879a996c23f Mon Sep 17 00:00:00 2001 From: Doug Rabson Date: Fri, 9 Sep 2022 13:33:35 +0100 Subject: libpod: Move NetworkDisconnect and NetworkConnect to networking_common.go This also moves Runtime methods ConnectContainerToNetwork and DisconnectContainerFromNetwork as well as support functions getFreeInterfaceName and normalizeNetworkName. [NO NEW TESTS NEEDED] Signed-off-by: Doug Rabson libpod: Move (Connect|Disconnect)Container(To|From)Network and normalizeNetworkName to networking_common.go [NO NEW TESTS NEEDED] Signed-off-by: Doug Rabson --- libpod/networking_common.go | 307 +++++++++++++++++++++++++++++++++++++++++++ libpod/networking_freebsd.go | 231 +------------------------------- libpod/networking_linux.go | 304 ------------------------------------------ 3 files changed, 309 insertions(+), 533 deletions(-) (limited to 'libpod') diff --git a/libpod/networking_common.go b/libpod/networking_common.go index e6509e66b..4f1947489 100644 --- a/libpod/networking_common.go +++ b/libpod/networking_common.go @@ -4,14 +4,21 @@ package libpod import ( + "errors" "fmt" "regexp" "sort" + "github.com/containers/common/libnetwork/etchosts" "github.com/containers/common/libnetwork/types" + "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/machine" + "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/libpod/events" "github.com/containers/podman/v4/pkg/namespaces" + "github.com/containers/podman/v4/pkg/rootless" + "github.com/containers/storage/pkg/lockfile" "github.com/sirupsen/logrus" ) @@ -345,3 +352,303 @@ func resultToBasicNetworkConfig(result types.StatusBlock) define.InspectBasicNet } return config } + +// NetworkDisconnect removes a container from the network +func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) error { + // only the bridge mode supports cni networks + if err := isBridgeNetMode(c.config.NetMode); err != nil { + return err + } + + c.lock.Lock() + defer c.lock.Unlock() + + networks, err := c.networks() + if err != nil { + return err + } + + // check if network exists and if the input is a ID we get the name + // CNI only uses names so it is important that we only use the name + netName, err = c.runtime.normalizeNetworkName(netName) + if err != nil { + return err + } + + _, nameExists := networks[netName] + if !nameExists && len(networks) > 0 { + return fmt.Errorf("container %s is not connected to network %s", nameOrID, netName) + } + + if err := c.syncContainer(); err != nil { + return err + } + // get network status before we disconnect + networkStatus := c.getNetworkStatus() + + if err := c.runtime.state.NetworkDisconnect(c, netName); err != nil { + return err + } + + c.newNetworkEvent(events.NetworkDisconnect, netName) + if !c.ensureState(define.ContainerStateRunning, define.ContainerStateCreated) { + return nil + } + + if c.state.NetNS == nil { + return fmt.Errorf("unable to disconnect %s from %s: %w", nameOrID, netName, define.ErrNoNetwork) + } + + opts := types.NetworkOptions{ + ContainerID: c.config.ID, + ContainerName: getCNIPodName(c), + } + opts.PortMappings = c.convertPortMappings() + opts.Networks = map[string]types.PerNetworkOptions{ + netName: networks[netName], + } + + if err := c.runtime.teardownNetwork(c.state.NetNS.Path(), opts); err != nil { + return err + } + + // update network status if container is running + oldStatus, statusExist := networkStatus[netName] + delete(networkStatus, netName) + c.state.NetworkStatus = networkStatus + err = c.save() + if err != nil { + return err + } + + // Reload ports when there are still connected networks, maybe we removed the network interface with the child ip. + // Reloading without connected networks does not make sense, so we can skip this step. + if rootless.IsRootless() && len(networkStatus) > 0 { + if err := c.reloadRootlessRLKPortMapping(); err != nil { + return err + } + } + + // Update resolv.conf if required + if statusExist { + stringIPs := make([]string, 0, len(oldStatus.DNSServerIPs)) + for _, ip := range oldStatus.DNSServerIPs { + stringIPs = append(stringIPs, ip.String()) + } + if len(stringIPs) > 0 { + logrus.Debugf("Removing DNS Servers %v from resolv.conf", stringIPs) + if err := c.removeNameserver(stringIPs); err != nil { + return err + } + } + + // update /etc/hosts file + if file, ok := c.state.BindMounts[config.DefaultHostsFile]; ok { + // sync the names with c.getHostsEntries() + names := []string{c.Hostname(), c.config.Name} + rm := etchosts.GetNetworkHostEntries(map[string]types.StatusBlock{netName: oldStatus}, names...) + if len(rm) > 0 { + // make sure to lock this file to prevent concurrent writes when + // this is used a net dependency container + lock, err := lockfile.GetLockfile(file) + if err != nil { + return fmt.Errorf("failed to lock hosts file: %w", err) + } + logrus.Debugf("Remove /etc/hosts entries %v", rm) + lock.Lock() + err = etchosts.Remove(file, rm) + lock.Unlock() + if err != nil { + return err + } + } + } + } + return nil +} + +// ConnectNetwork connects a container to a given network +func (c *Container) NetworkConnect(nameOrID, netName string, netOpts types.PerNetworkOptions) error { + // only the bridge mode supports cni networks + if err := isBridgeNetMode(c.config.NetMode); err != nil { + return err + } + + c.lock.Lock() + defer c.lock.Unlock() + + networks, err := c.networks() + if err != nil { + return err + } + + // check if network exists and if the input is a ID we get the name + // CNI only uses names so it is important that we only use the name + netName, err = c.runtime.normalizeNetworkName(netName) + if err != nil { + return err + } + + if err := c.syncContainer(); err != nil { + return err + } + + // get network status before we connect + networkStatus := c.getNetworkStatus() + + // always add the short id as alias for docker compat + netOpts.Aliases = append(netOpts.Aliases, c.config.ID[:12]) + + if netOpts.InterfaceName == "" { + netOpts.InterfaceName = getFreeInterfaceName(networks) + if netOpts.InterfaceName == "" { + return errors.New("could not find free network interface name") + } + } + + if err := c.runtime.state.NetworkConnect(c, netName, netOpts); err != nil { + // Docker compat: treat requests to attach already attached networks as a no-op, ignoring opts + if errors.Is(err, define.ErrNetworkConnected) && c.ensureState(define.ContainerStateConfigured) { + return nil + } + + return err + } + c.newNetworkEvent(events.NetworkConnect, netName) + if !c.ensureState(define.ContainerStateRunning, define.ContainerStateCreated) { + return nil + } + if c.state.NetNS == nil { + return fmt.Errorf("unable to connect %s to %s: %w", nameOrID, netName, define.ErrNoNetwork) + } + + opts := types.NetworkOptions{ + ContainerID: c.config.ID, + ContainerName: getCNIPodName(c), + } + opts.PortMappings = c.convertPortMappings() + opts.Networks = map[string]types.PerNetworkOptions{ + netName: netOpts, + } + + results, err := c.runtime.setUpNetwork(c.state.NetNS.Path(), opts) + if err != nil { + return err + } + if len(results) != 1 { + return errors.New("when adding aliases, results must be of length 1") + } + + // we need to get the old host entries before we add the new one to the status + // if we do not add do it here we will get the wrong existing entries which will throw of the logic + // we could also copy the map but this does not seem worth it + // sync the hostNames with c.getHostsEntries() + hostNames := []string{c.Hostname(), c.config.Name} + oldHostEntries := etchosts.GetNetworkHostEntries(networkStatus, hostNames...) + + // update network status + if networkStatus == nil { + networkStatus = make(map[string]types.StatusBlock, 1) + } + networkStatus[netName] = results[netName] + c.state.NetworkStatus = networkStatus + + err = c.save() + if err != nil { + return err + } + + // The first network needs a port reload to set the correct child ip for the rootlessport process. + // Adding a second network does not require a port reload because the child ip is still valid. + if rootless.IsRootless() && len(networks) == 0 { + if err := c.reloadRootlessRLKPortMapping(); err != nil { + return err + } + } + + ipv6, err := c.checkForIPv6(networkStatus) + if err != nil { + return err + } + + // Update resolv.conf if required + stringIPs := make([]string, 0, len(results[netName].DNSServerIPs)) + for _, ip := range results[netName].DNSServerIPs { + if (ip.To4() == nil) && !ipv6 { + continue + } + stringIPs = append(stringIPs, ip.String()) + } + if len(stringIPs) > 0 { + logrus.Debugf("Adding DNS Servers %v to resolv.conf", stringIPs) + if err := c.addNameserver(stringIPs); err != nil { + return err + } + } + + // update /etc/hosts file + if file, ok := c.state.BindMounts[config.DefaultHostsFile]; ok { + // make sure to lock this file to prevent concurrent writes when + // this is used a net dependency container + lock, err := lockfile.GetLockfile(file) + if err != nil { + return fmt.Errorf("failed to lock hosts file: %w", err) + } + new := etchosts.GetNetworkHostEntries(results, hostNames...) + logrus.Debugf("Add /etc/hosts entries %v", new) + // use special AddIfExists API to make sure we only add new entries if an old one exists + // see the AddIfExists() comment for more information + lock.Lock() + err = etchosts.AddIfExists(file, oldHostEntries, new) + lock.Unlock() + if err != nil { + return err + } + } + + return nil +} + +// get a free interface name for a new network +// return an empty string if no free name was found +func getFreeInterfaceName(networks map[string]types.PerNetworkOptions) string { + ifNames := make([]string, 0, len(networks)) + for _, opts := range networks { + ifNames = append(ifNames, opts.InterfaceName) + } + for i := 0; i < 100000; i++ { + ifName := fmt.Sprintf("eth%d", i) + if !util.StringInSlice(ifName, ifNames) { + return ifName + } + } + return "" +} + +// DisconnectContainerFromNetwork removes a container from its CNI network +func (r *Runtime) DisconnectContainerFromNetwork(nameOrID, netName string, force bool) error { + ctr, err := r.LookupContainer(nameOrID) + if err != nil { + return err + } + return ctr.NetworkDisconnect(nameOrID, netName, force) +} + +// ConnectContainerToNetwork connects a container to a CNI network +func (r *Runtime) ConnectContainerToNetwork(nameOrID, netName string, netOpts types.PerNetworkOptions) error { + ctr, err := r.LookupContainer(nameOrID) + if err != nil { + return err + } + return ctr.NetworkConnect(nameOrID, netName, netOpts) +} + +// normalizeNetworkName takes a network name, a partial or a full network ID and returns the network name. +// If the network is not found a errors is returned. +func (r *Runtime) normalizeNetworkName(nameOrID string) (string, error) { + net, err := r.network.NetworkInspect(nameOrID) + if err != nil { + return "", err + } + return net.Name, nil +} diff --git a/libpod/networking_freebsd.go b/libpod/networking_freebsd.go index b6bcb3f71..3023bcac9 100644 --- a/libpod/networking_freebsd.go +++ b/libpod/networking_freebsd.go @@ -15,9 +15,6 @@ import ( "github.com/containers/buildah/pkg/jail" "github.com/containers/common/libnetwork/types" - "github.com/containers/podman/v4/libpod/define" - "github.com/containers/podman/v4/libpod/events" - "github.com/containers/podman/v4/pkg/util" "github.com/containers/storage/pkg/lockfile" "github.com/sirupsen/logrus" ) @@ -263,232 +260,8 @@ func (c *Container) inspectJoinedNetworkNS(networkns string) (q types.StatusBloc } -// NetworkDisconnect removes a container from the network -func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) error { - // only the bridge mode supports cni networks - if err := isBridgeNetMode(c.config.NetMode); err != nil { - return err - } - - c.lock.Lock() - defer c.lock.Unlock() - - networks, err := c.networks() - if err != nil { - return err - } - - // check if network exists and if the input is a ID we get the name - // CNI only uses names so it is important that we only use the name - netName, err = c.runtime.normalizeNetworkName(netName) - if err != nil { - return err - } - - _, nameExists := networks[netName] - if !nameExists && len(networks) > 0 { - return fmt.Errorf("container %s is not connected to network %s", nameOrID, netName) - } - - if err := c.syncContainer(); err != nil { - return err - } - // get network status before we disconnect - networkStatus := c.getNetworkStatus() - - if err := c.runtime.state.NetworkDisconnect(c, netName); err != nil { - return err - } - - c.newNetworkEvent(events.NetworkDisconnect, netName) - if !c.ensureState(define.ContainerStateRunning, define.ContainerStateCreated) { - return nil - } - - opts := types.NetworkOptions{ - ContainerID: c.config.ID, - ContainerName: getCNIPodName(c), - } - opts.PortMappings = c.convertPortMappings() - opts.Networks = map[string]types.PerNetworkOptions{ - netName: networks[netName], - } - - // update network status if container is running - oldStatus, statusExist := networkStatus[netName] - delete(networkStatus, netName) - c.state.NetworkStatus = networkStatus - err = c.save() - if err != nil { - return err - } - - // Update resolv.conf if required - if statusExist { - stringIPs := make([]string, 0, len(oldStatus.DNSServerIPs)) - for _, ip := range oldStatus.DNSServerIPs { - stringIPs = append(stringIPs, ip.String()) - } - if len(stringIPs) == 0 { - return nil - } - logrus.Debugf("Removing DNS Servers %v from resolv.conf", stringIPs) - if err := c.removeNameserver(stringIPs); err != nil { - return err - } - } - - return nil -} - -// ConnectNetwork connects a container to a given network -func (c *Container) NetworkConnect(nameOrID, netName string, netOpts types.PerNetworkOptions) error { - // only the bridge mode supports cni networks - if err := isBridgeNetMode(c.config.NetMode); err != nil { - return err - } - - c.lock.Lock() - defer c.lock.Unlock() - - networks, err := c.networks() - if err != nil { - return err - } - - // check if network exists and if the input is a ID we get the name - // CNI only uses names so it is important that we only use the name - netName, err = c.runtime.normalizeNetworkName(netName) - if err != nil { - return err - } - - if err := c.syncContainer(); err != nil { - return err - } - - // get network status before we connect - networkStatus := c.getNetworkStatus() - - // always add the short id as alias for docker compat - netOpts.Aliases = append(netOpts.Aliases, c.config.ID[:12]) - - if netOpts.InterfaceName == "" { - netOpts.InterfaceName = getFreeInterfaceName(networks) - if netOpts.InterfaceName == "" { - return errors.New("could not find free network interface name") - } - } - - if err := c.runtime.state.NetworkConnect(c, netName, netOpts); err != nil { - return err - } - c.newNetworkEvent(events.NetworkConnect, netName) - if !c.ensureState(define.ContainerStateRunning, define.ContainerStateCreated) { - return nil - } - - opts := types.NetworkOptions{ - ContainerID: c.config.ID, - ContainerName: getCNIPodName(c), - } - opts.PortMappings = c.convertPortMappings() - opts.Networks = map[string]types.PerNetworkOptions{ - netName: netOpts, - } - - /* - results, err := c.runtime.setUpNetwork(c.state.NetNS.Path(), opts) - if err != nil { - return err - } - if len(results) != 1 { - return errors.New("when adding aliases, results must be of length 1") - } - */ - var results map[string]types.StatusBlock - - // update network status - if networkStatus == nil { - networkStatus = make(map[string]types.StatusBlock, 1) - } - networkStatus[netName] = results[netName] - c.state.NetworkStatus = networkStatus - - err = c.save() - if err != nil { - return err - } - - // The first network needs a port reload to set the correct child ip for the rootlessport process. - // Adding a second network does not require a port reload because the child ip is still valid. - - ipv6, err := c.checkForIPv6(networkStatus) - if err != nil { - return err - } - - // Update resolv.conf if required - stringIPs := make([]string, 0, len(results[netName].DNSServerIPs)) - for _, ip := range results[netName].DNSServerIPs { - if (ip.To4() == nil) && !ipv6 { - continue - } - stringIPs = append(stringIPs, ip.String()) - } - if len(stringIPs) == 0 { - return nil - } - logrus.Debugf("Adding DNS Servers %v to resolv.conf", stringIPs) - if err := c.addNameserver(stringIPs); err != nil { - return err - } - - return nil -} - -// get a free interface name for a new network -// return an empty string if no free name was found -func getFreeInterfaceName(networks map[string]types.PerNetworkOptions) string { - ifNames := make([]string, 0, len(networks)) - for _, opts := range networks { - ifNames = append(ifNames, opts.InterfaceName) - } - for i := 0; i < 100000; i++ { - ifName := fmt.Sprintf("eth%d", i) - if !util.StringInSlice(ifName, ifNames) { - return ifName - } - } - return "" -} - -// DisconnectContainerFromNetwork removes a container from its CNI network -func (r *Runtime) DisconnectContainerFromNetwork(nameOrID, netName string, force bool) error { - ctr, err := r.LookupContainer(nameOrID) - if err != nil { - return err - } - return ctr.NetworkDisconnect(nameOrID, netName, force) -} - -// ConnectContainerToNetwork connects a container to a CNI network -func (r *Runtime) ConnectContainerToNetwork(nameOrID, netName string, netOpts types.PerNetworkOptions) error { - ctr, err := r.LookupContainer(nameOrID) - if err != nil { - return err - } - return ctr.NetworkConnect(nameOrID, netName, netOpts) -} - -// normalizeNetworkName takes a network name, a partial or a full network ID and returns the network name. -// If the network is not found a errors is returned. -func (r *Runtime) normalizeNetworkName(nameOrID string) (string, error) { - net, err := r.network.NetworkInspect(nameOrID) - if err != nil { - return "", err - } - return net.Name, nil +func (c *Container) reloadRootlessRLKPortMapping() error { + return errors.New("unsupported (*Container).reloadRootlessRLKPortMapping") } // ocicniPortsToNetTypesPorts convert the old port format to the new one diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 7eae5c396..2052ac0c0 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -20,14 +20,10 @@ import ( "time" "github.com/containernetworking/plugins/pkg/ns" - "github.com/containers/common/libnetwork/etchosts" "github.com/containers/common/libnetwork/resolvconf" "github.com/containers/common/libnetwork/types" - "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/netns" "github.com/containers/common/pkg/util" - "github.com/containers/podman/v4/libpod/define" - "github.com/containers/podman/v4/libpod/events" "github.com/containers/podman/v4/pkg/errorhandling" "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/utils" @@ -869,306 +865,6 @@ func (w *logrusDebugWriter) Write(p []byte) (int, error) { return len(p), nil } -// NetworkDisconnect removes a container from the network -func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) error { - // only the bridge mode supports cni networks - if err := isBridgeNetMode(c.config.NetMode); err != nil { - return err - } - - c.lock.Lock() - defer c.lock.Unlock() - - networks, err := c.networks() - if err != nil { - return err - } - - // check if network exists and if the input is a ID we get the name - // CNI only uses names so it is important that we only use the name - netName, err = c.runtime.normalizeNetworkName(netName) - if err != nil { - return err - } - - _, nameExists := networks[netName] - if !nameExists && len(networks) > 0 { - return fmt.Errorf("container %s is not connected to network %s", nameOrID, netName) - } - - if err := c.syncContainer(); err != nil { - return err - } - // get network status before we disconnect - networkStatus := c.getNetworkStatus() - - if err := c.runtime.state.NetworkDisconnect(c, netName); err != nil { - return err - } - - c.newNetworkEvent(events.NetworkDisconnect, netName) - if !c.ensureState(define.ContainerStateRunning, define.ContainerStateCreated) { - return nil - } - - if c.state.NetNS == nil { - return fmt.Errorf("unable to disconnect %s from %s: %w", nameOrID, netName, define.ErrNoNetwork) - } - - opts := types.NetworkOptions{ - ContainerID: c.config.ID, - ContainerName: getCNIPodName(c), - } - opts.PortMappings = c.convertPortMappings() - opts.Networks = map[string]types.PerNetworkOptions{ - netName: networks[netName], - } - - if err := c.runtime.teardownNetwork(c.state.NetNS.Path(), opts); err != nil { - return err - } - - // update network status if container is running - oldStatus, statusExist := networkStatus[netName] - delete(networkStatus, netName) - c.state.NetworkStatus = networkStatus - err = c.save() - if err != nil { - return err - } - - // Reload ports when there are still connected networks, maybe we removed the network interface with the child ip. - // Reloading without connected networks does not make sense, so we can skip this step. - if rootless.IsRootless() && len(networkStatus) > 0 { - if err := c.reloadRootlessRLKPortMapping(); err != nil { - return err - } - } - - // Update resolv.conf if required - if statusExist { - stringIPs := make([]string, 0, len(oldStatus.DNSServerIPs)) - for _, ip := range oldStatus.DNSServerIPs { - stringIPs = append(stringIPs, ip.String()) - } - if len(stringIPs) > 0 { - logrus.Debugf("Removing DNS Servers %v from resolv.conf", stringIPs) - if err := c.removeNameserver(stringIPs); err != nil { - return err - } - } - - // update /etc/hosts file - if file, ok := c.state.BindMounts[config.DefaultHostsFile]; ok { - // sync the names with c.getHostsEntries() - names := []string{c.Hostname(), c.config.Name} - rm := etchosts.GetNetworkHostEntries(map[string]types.StatusBlock{netName: oldStatus}, names...) - if len(rm) > 0 { - // make sure to lock this file to prevent concurrent writes when - // this is used a net dependency container - lock, err := lockfile.GetLockfile(file) - if err != nil { - return fmt.Errorf("failed to lock hosts file: %w", err) - } - logrus.Debugf("Remove /etc/hosts entries %v", rm) - lock.Lock() - err = etchosts.Remove(file, rm) - lock.Unlock() - if err != nil { - return err - } - } - } - } - return nil -} - -// ConnectNetwork connects a container to a given network -func (c *Container) NetworkConnect(nameOrID, netName string, netOpts types.PerNetworkOptions) error { - // only the bridge mode supports cni networks - if err := isBridgeNetMode(c.config.NetMode); err != nil { - return err - } - - c.lock.Lock() - defer c.lock.Unlock() - - networks, err := c.networks() - if err != nil { - return err - } - - // check if network exists and if the input is a ID we get the name - // CNI only uses names so it is important that we only use the name - netName, err = c.runtime.normalizeNetworkName(netName) - if err != nil { - return err - } - - if err := c.syncContainer(); err != nil { - return err - } - - // get network status before we connect - networkStatus := c.getNetworkStatus() - - // always add the short id as alias for docker compat - netOpts.Aliases = append(netOpts.Aliases, c.config.ID[:12]) - - if netOpts.InterfaceName == "" { - netOpts.InterfaceName = getFreeInterfaceName(networks) - if netOpts.InterfaceName == "" { - return errors.New("could not find free network interface name") - } - } - - if err := c.runtime.state.NetworkConnect(c, netName, netOpts); err != nil { - // Docker compat: treat requests to attach already attached networks as a no-op, ignoring opts - if errors.Is(err, define.ErrNetworkConnected) && c.ensureState(define.ContainerStateConfigured) { - return nil - } - - return err - } - c.newNetworkEvent(events.NetworkConnect, netName) - if !c.ensureState(define.ContainerStateRunning, define.ContainerStateCreated) { - return nil - } - if c.state.NetNS == nil { - return fmt.Errorf("unable to connect %s to %s: %w", nameOrID, netName, define.ErrNoNetwork) - } - - opts := types.NetworkOptions{ - ContainerID: c.config.ID, - ContainerName: getCNIPodName(c), - } - opts.PortMappings = c.convertPortMappings() - opts.Networks = map[string]types.PerNetworkOptions{ - netName: netOpts, - } - - results, err := c.runtime.setUpNetwork(c.state.NetNS.Path(), opts) - if err != nil { - return err - } - if len(results) != 1 { - return errors.New("when adding aliases, results must be of length 1") - } - - // we need to get the old host entries before we add the new one to the status - // if we do not add do it here we will get the wrong existing entries which will throw of the logic - // we could also copy the map but this does not seem worth it - // sync the hostNames with c.getHostsEntries() - hostNames := []string{c.Hostname(), c.config.Name} - oldHostEntries := etchosts.GetNetworkHostEntries(networkStatus, hostNames...) - - // update network status - if networkStatus == nil { - networkStatus = make(map[string]types.StatusBlock, 1) - } - networkStatus[netName] = results[netName] - c.state.NetworkStatus = networkStatus - - err = c.save() - if err != nil { - return err - } - - // The first network needs a port reload to set the correct child ip for the rootlessport process. - // Adding a second network does not require a port reload because the child ip is still valid. - if rootless.IsRootless() && len(networks) == 0 { - if err := c.reloadRootlessRLKPortMapping(); err != nil { - return err - } - } - - ipv6, err := c.checkForIPv6(networkStatus) - if err != nil { - return err - } - - // Update resolv.conf if required - stringIPs := make([]string, 0, len(results[netName].DNSServerIPs)) - for _, ip := range results[netName].DNSServerIPs { - if (ip.To4() == nil) && !ipv6 { - continue - } - stringIPs = append(stringIPs, ip.String()) - } - if len(stringIPs) > 0 { - logrus.Debugf("Adding DNS Servers %v to resolv.conf", stringIPs) - if err := c.addNameserver(stringIPs); err != nil { - return err - } - } - - // update /etc/hosts file - if file, ok := c.state.BindMounts[config.DefaultHostsFile]; ok { - // make sure to lock this file to prevent concurrent writes when - // this is used a net dependency container - lock, err := lockfile.GetLockfile(file) - if err != nil { - return fmt.Errorf("failed to lock hosts file: %w", err) - } - new := etchosts.GetNetworkHostEntries(results, hostNames...) - logrus.Debugf("Add /etc/hosts entries %v", new) - // use special AddIfExists API to make sure we only add new entries if an old one exists - // see the AddIfExists() comment for more information - lock.Lock() - err = etchosts.AddIfExists(file, oldHostEntries, new) - lock.Unlock() - if err != nil { - return err - } - } - - return nil -} - -// get a free interface name for a new network -// return an empty string if no free name was found -func getFreeInterfaceName(networks map[string]types.PerNetworkOptions) string { - ifNames := make([]string, 0, len(networks)) - for _, opts := range networks { - ifNames = append(ifNames, opts.InterfaceName) - } - for i := 0; i < 100000; i++ { - ifName := fmt.Sprintf("eth%d", i) - if !util.StringInSlice(ifName, ifNames) { - return ifName - } - } - return "" -} - -// DisconnectContainerFromNetwork removes a container from its CNI network -func (r *Runtime) DisconnectContainerFromNetwork(nameOrID, netName string, force bool) error { - ctr, err := r.LookupContainer(nameOrID) - if err != nil { - return err - } - return ctr.NetworkDisconnect(nameOrID, netName, force) -} - -// ConnectContainerToNetwork connects a container to a CNI network -func (r *Runtime) ConnectContainerToNetwork(nameOrID, netName string, netOpts types.PerNetworkOptions) error { - ctr, err := r.LookupContainer(nameOrID) - if err != nil { - return err - } - return ctr.NetworkConnect(nameOrID, netName, netOpts) -} - -// normalizeNetworkName takes a network name, a partial or a full network ID and returns the network name. -// If the network is not found a errors is returned. -func (r *Runtime) normalizeNetworkName(nameOrID string) (string, error) { - net, err := r.network.NetworkInspect(nameOrID) - if err != nil { - return "", err - } - return net.Name, nil -} - // ocicniPortsToNetTypesPorts convert the old port format to the new one // while deduplicating ports into ranges func ocicniPortsToNetTypesPorts(ports []types.OCICNIPortMapping) []types.PortMapping { -- cgit v1.2.3-54-g00ecf