diff options
author | Paul Holzinger <pholzing@redhat.com> | 2021-08-16 16:11:26 +0200 |
---|---|---|
committer | Paul Holzinger <pholzing@redhat.com> | 2021-09-15 20:00:20 +0200 |
commit | 85e8fbf7f33717ef6a0d6cf9e2143b52c874c2de (patch) | |
tree | 82b0c29102d2779c18ea8a6f10df5dc1139e3817 /libpod | |
parent | 218f132fdf4939d9e0374ef860d534f19e71df54 (diff) | |
download | podman-85e8fbf7f33717ef6a0d6cf9e2143b52c874c2de.tar.gz podman-85e8fbf7f33717ef6a0d6cf9e2143b52c874c2de.tar.bz2 podman-85e8fbf7f33717ef6a0d6cf9e2143b52c874c2de.zip |
Wire network interface into libpod
Make use of the new network interface in libpod.
This commit contains several breaking changes:
- podman network create only outputs the new network name and not file
path.
- podman network ls shows the network driver instead of the cni version
and plugins.
- podman network inspect outputs the new network struct and not the cni
conflist.
- The bindings and libpod api endpoints have been changed to use the new
network structure.
The container network status is stored in a new field in the state. The
status should be received with the new `c.getNetworkStatus`. This will
migrate the old status to the new format. Therefore old containers should
contine to work correctly in all cases even when network connect/
disconnect is used.
New features:
- podman network reload keeps the ip and mac for more than one network.
- podman container restore keeps the ip and mac for more than one
network.
- The network create compat endpoint can now use more than one ipam
config.
The man pages and the swagger doc are updated to reflect the latest
changes.
Signed-off-by: Paul Holzinger <pholzing@redhat.com>
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/boltdb_state.go | 2 | ||||
-rw-r--r-- | libpod/container.go | 127 | ||||
-rw-r--r-- | libpod/container_internal.go | 79 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 118 | ||||
-rw-r--r-- | libpod/info.go | 4 | ||||
-rw-r--r-- | libpod/network/cni/run.go | 7 | ||||
-rw-r--r-- | libpod/network/config.go | 159 | ||||
-rw-r--r-- | libpod/network/create.go | 310 | ||||
-rw-r--r-- | libpod/network/create_test.go | 130 | ||||
-rw-r--r-- | libpod/network/devices.go | 59 | ||||
-rw-r--r-- | libpod/network/files.go | 211 | ||||
-rw-r--r-- | libpod/network/ip.go | 19 | ||||
-rw-r--r-- | libpod/network/lock.go | 35 | ||||
-rw-r--r-- | libpod/network/netconflist.go | 312 | ||||
-rw-r--r-- | libpod/network/netconflist_test.go | 109 | ||||
-rw-r--r-- | libpod/network/network.go | 288 | ||||
-rw-r--r-- | libpod/network/network_test.go | 35 | ||||
-rw-r--r-- | libpod/network/subnet.go | 78 | ||||
-rw-r--r-- | libpod/network/subnet_test.go | 97 | ||||
-rw-r--r-- | libpod/network/types/network.go | 9 | ||||
-rw-r--r-- | libpod/network/util/filters.go | 40 | ||||
-rw-r--r-- | libpod/networking_linux.go | 507 | ||||
-rw-r--r-- | libpod/networking_slirp4netns.go | 17 | ||||
-rw-r--r-- | libpod/runtime.go | 37 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 5 |
25 files changed, 452 insertions, 2342 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index 5df3e8961..56b4bafd3 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -1223,7 +1223,7 @@ func (s *BoltState) NetworkConnect(ctr *Container, network string, aliases []str } ctrNetworks := ctr.config.Networks if len(ctrNetworks) == 0 { - ctrNetworks = []string{ctr.runtime.netPlugin.GetDefaultNetworkName()} + ctrNetworks = []string{ctr.runtime.config.Network.DefaultNetwork} } // Copy in all the container's CNI networks for _, net := range ctrNetworks { diff --git a/libpod/container.go b/libpod/container.go index a4bbb5dd0..28bf3da07 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -8,12 +8,13 @@ import ( "os" "time" - "github.com/containernetworking/cni/pkg/types" cnitypes "github.com/containernetworking/cni/pkg/types/current" "github.com/containers/common/pkg/secrets" "github.com/containers/image/v5/manifest" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/lock" + "github.com/containers/podman/v3/libpod/network/cni" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/storage" "github.com/cri-o/ocicni/pkg/ocicni" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -114,14 +115,11 @@ type Container struct { rootlessPortSyncR *os.File rootlessPortSyncW *os.File - // A restored container should have the same IP address as before - // being checkpointed. If requestedIP is set it will be used instead - // of config.StaticIP. - requestedIP net.IP - // A restored container should have the same MAC address as before - // being checkpointed. If requestedMAC is set it will be used instead - // of config.StaticMAC. - requestedMAC net.HardwareAddr + // perNetworkOpts should be set when you want to use special network + // options when calling network setup/teardown. This should be used for + // container restore or network reload for example. Leave this nil if + // the settings from the container config should be used. + perNetworkOpts map[string]types.PerNetworkOptions // This is true if a container is restored from a checkpoint. restoreFromCheckpoint bool @@ -173,11 +171,20 @@ type ContainerState struct { // Podman. // These are DEPRECATED and will be removed in a future release. LegacyExecSessions map[string]*legacyExecSession `json:"execSessions,omitempty"` - // NetworkStatus contains the configuration results for all networks + // NetworkStatusOld contains the configuration results for all networks // the pod is attached to. Only populated if we created a network // namespace for the container, and the network namespace is currently - // active - NetworkStatus []*cnitypes.Result `json:"networkResults,omitempty"` + // active. + // These are DEPRECATED and will be removed in a future release. + // This field is only used for backwarts compatibility. + NetworkStatusOld []*cnitypes.Result `json:"networkResults,omitempty"` + // NetworkStatus contains the network Status for all networks + // the container is attached to. Only populated if we created a network + // namespace for the container, and the network namespace is currently + // active. + // To read this field use container.getNetworkStatus() instead, this will + // take care of migrating the old DEPRECATED network status to the new format. + NetworkStatus map[string]types.StatusBlock `json:"networkStatus,omitempty"` // BindMounts contains files that will be bind-mounted into the // container when it is mounted. // These include /etc/hosts and /etc/resolv.conf @@ -788,66 +795,6 @@ func (c *Container) ExecSession(id string) (*ExecSession, error) { return returnSession, nil } -// IPs retrieves a container's IP address(es) -// This will only be populated if the container is configured to created a new -// network namespace, and that namespace is presently active -func (c *Container) IPs() ([]net.IPNet, error) { - if !c.batched { - c.lock.Lock() - defer c.lock.Unlock() - - if err := c.syncContainer(); err != nil { - return nil, err - } - } - - if !c.config.CreateNetNS { - return nil, errors.Wrapf(define.ErrInvalidArg, "container %s network namespace is not managed by libpod", c.ID()) - } - - ips := make([]net.IPNet, 0) - - for _, r := range c.state.NetworkStatus { - for _, ip := range r.IPs { - ips = append(ips, ip.Address) - } - } - - return ips, nil -} - -// Routes retrieves a container's routes -// This will only be populated if the container is configured to created a new -// network namespace, and that namespace is presently active -func (c *Container) Routes() ([]types.Route, error) { - if !c.batched { - c.lock.Lock() - defer c.lock.Unlock() - - if err := c.syncContainer(); err != nil { - return nil, err - } - } - - if !c.config.CreateNetNS { - return nil, errors.Wrapf(define.ErrInvalidArg, "container %s network namespace is not managed by libpod", c.ID()) - } - - routes := make([]types.Route, 0) - - for _, r := range c.state.NetworkStatus { - for _, route := range r.Routes { - newRoute := types.Route{ - Dst: route.Dst, - GW: route.GW, - } - routes = append(routes, newRoute) - } - } - - return routes, nil -} - // BindMounts retrieves bind mounts that were created by libpod and will be // added to the container // All these mounts except /dev/shm are ignored if a mount in the given spec has @@ -1230,7 +1177,7 @@ func (c *Container) networks() ([]string, bool, error) { networks, err := c.runtime.state.GetNetworks(c) if err != nil && errors.Cause(err) == define.ErrNoSuchNetwork { if len(c.config.Networks) == 0 && c.config.NetMode.IsBridge() { - return []string{c.runtime.netPlugin.GetDefaultNetworkName()}, true, nil + return []string{c.runtime.config.Network.DefaultNetwork}, true, nil } return c.config.Networks, false, nil } @@ -1267,3 +1214,37 @@ func (d ContainerNetworkDescriptions) getInterfaceByName(networkName string) (st } return fmt.Sprintf("eth%d", val), exists } + +// getNetworkStatus get the current network status from the state. If the container +// still uses the old network status it is converted to the new format. This function +// should be used instead of reading c.state.NetworkStatus directly. +func (c *Container) getNetworkStatus() map[string]types.StatusBlock { + if c.state.NetworkStatus != nil { + return c.state.NetworkStatus + } + if c.state.NetworkStatusOld != nil { + // Note: NetworkStatusOld does not contain the network names so we get them extra + // Generally the order should be the same + networks, _, err := c.networks() + if err != nil { + return nil + } + if len(networks) != len(c.state.NetworkStatusOld) { + return nil + } + result := make(map[string]types.StatusBlock, len(c.state.NetworkStatusOld)) + for i := range c.state.NetworkStatusOld { + status, err := cni.CNIResultToStatus(c.state.NetworkStatusOld[i]) + if err != nil { + return nil + } + result[networks[i]] = status + } + c.state.NetworkStatus = result + _ = c.save() + // TODO remove debug for final version + logrus.Debugf("converted old network result to new result %v", result) + return result + } + return nil +} diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 1033729ae..9ac2cd5bd 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -644,17 +644,8 @@ func (c *Container) refresh() error { } c.lock = lock - // Try to delete any lingering IP allocations. - // If this fails, just log and ignore. - // I'm a little concerned that this is so far down in refresh() and we - // could fail before getting to it - but the worst that would happen is - // that Inspect() would return info on IPs we no longer own. - if len(c.state.NetworkStatus) > 0 { - if err := c.removeIPv4Allocations(); err != nil { - logrus.Errorf("Error removing IP allocations for container %s: %v", c.ID(), err) - } - } c.state.NetworkStatus = nil + c.state.NetworkStatusOld = nil if err := c.save(); err != nil { return errors.Wrapf(err, "error refreshing state for container %s", c.ID()) @@ -668,57 +659,6 @@ func (c *Container) refresh() error { return nil } -// Try and remove IP address allocations. Presently IPv4 only. -// Should be safe as rootless because NetworkStatus should only be populated if -// CNI is running. -func (c *Container) removeIPv4Allocations() error { - cniNetworksDir, err := getCNINetworksDir() - if err != nil { - return err - } - - if len(c.state.NetworkStatus) == 0 { - return nil - } - - cniDefaultNetwork := "" - if c.runtime.netPlugin != nil { - cniDefaultNetwork = c.runtime.netPlugin.GetDefaultNetworkName() - } - - networks, _, err := c.networks() - if err != nil { - return err - } - - if len(networks) != len(c.state.NetworkStatus) { - return errors.Wrapf(define.ErrInternal, "network mismatch: asked to join %d CNI networks but got %d CNI results", len(networks), len(c.state.NetworkStatus)) - } - - for index, result := range c.state.NetworkStatus { - for _, ctrIP := range result.IPs { - if ctrIP.Version != "4" { - continue - } - candidate := "" - if len(networks) > 0 { - // CNI returns networks in order we passed them. - // So our index into results should be our index - // into networks. - candidate = filepath.Join(cniNetworksDir, networks[index], ctrIP.Address.IP.String()) - } else { - candidate = filepath.Join(cniNetworksDir, cniDefaultNetwork, ctrIP.Address.IP.String()) - } - logrus.Debugf("Going to try removing IP address reservation file %q for container %s", candidate, c.ID()) - if err := os.Remove(candidate); err != nil && !os.IsNotExist(err) { - return errors.Wrapf(err, "error removing CNI IP reservation file %q for container %s", candidate, c.ID()) - } - } - } - - return nil -} - // Remove conmon attach socket and terminal resize FIFO // This is necessary for restarting containers func (c *Container) removeConmonFiles() error { @@ -1017,11 +957,9 @@ func (c *Container) completeNetworkSetup() error { } state := c.state // collect any dns servers that cni tells us to use (dnsname) - for _, cni := range state.NetworkStatus { - if cni.DNS.Nameservers != nil { - for _, server := range cni.DNS.Nameservers { - outResolvConf = append(outResolvConf, fmt.Sprintf("nameserver %s", server)) - } + for _, status := range c.getNetworkStatus() { + for _, server := range status.DNSServerIPs { + outResolvConf = append(outResolvConf, fmt.Sprintf("nameserver %s", server)) } } // check if we have a bindmount for /etc/hosts @@ -1062,9 +1000,12 @@ func (c *Container) completeNetworkSetup() error { func (c *Container) cniHosts() string { var hosts string - if len(c.state.NetworkStatus) > 0 && len(c.state.NetworkStatus[0].IPs) > 0 { - ipAddress := strings.Split(c.state.NetworkStatus[0].IPs[0].Address.String(), "/")[0] - hosts += fmt.Sprintf("%s\t%s %s\n", ipAddress, c.Hostname(), c.Config().Name) + for _, status := range c.getNetworkStatus() { + for _, netInt := range status.Interfaces { + for _, netAddress := range netInt.Networks { + hosts += fmt.Sprintf("%s\t%s %s\n", netAddress.Subnet.IP.String(), c.Hostname(), c.Config().Name) + } + } } return hosts } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 4194a0d93..0557b30d0 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -22,7 +22,6 @@ import ( metadata "github.com/checkpoint-restore/checkpointctl/lib" cdi "github.com/container-orchestrated-devices/container-device-interface/pkg" - cnitypes "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/plugins/pkg/ns" "github.com/containers/buildah/pkg/chrootuser" "github.com/containers/buildah/pkg/overlay" @@ -34,6 +33,7 @@ import ( "github.com/containers/common/pkg/umask" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/annotations" "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/checkpoint/crutils" @@ -81,7 +81,7 @@ func (c *Container) prepare() error { var ( wg sync.WaitGroup netNS ns.NetNS - networkStatus []*cnitypes.Result + networkStatus map[string]types.StatusBlock createNetNSErr, mountStorageErr error mountPoint string tmpStateLock sync.Mutex @@ -263,6 +263,7 @@ func (c *Container) cleanupNetwork() error { c.state.NetNS = nil c.state.NetworkStatus = nil + c.state.NetworkStatusOld = nil if c.valid { return c.save() @@ -1121,7 +1122,8 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO // Save network.status. This is needed to restore the container with // the same IP. Currently limited to one IP address in a container // with one interface. - if _, err := metadata.WriteJSONFile(c.state.NetworkStatus, c.bundlePath(), metadata.NetworkStatusFile); err != nil { + // FIXME: will this break something? + if _, err := metadata.WriteJSONFile(c.getNetworkStatus(), c.bundlePath(), metadata.NetworkStatusFile); err != nil { return err } @@ -1261,8 +1263,11 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti } // Read network configuration from checkpoint - // Currently only one interface with one IP is supported. - networkStatus, _, err := metadata.ReadContainerCheckpointNetworkStatus(c.bundlePath()) + var netStatus map[string]types.StatusBlock + _, err := metadata.ReadJSONFile(&netStatus, c.bundlePath(), metadata.NetworkStatusFile) + if err != nil { + logrus.Infof("failed to unmarshal network status, cannot restore the same ip/mac: %v", err) + } // If the restored container should get a new name, the IP address of // the container will not be restored. This assumes that if a new name is // specified, the container is restored multiple times. @@ -1271,19 +1276,41 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti // best solution. if err == nil && options.Name == "" && (!options.IgnoreStaticIP || !options.IgnoreStaticMAC) { // The file with the network.status does exist. Let's restore the - // container with the same IP address / MAC address as during checkpointing. - if !options.IgnoreStaticIP { - if IP := metadata.GetIPFromNetworkStatus(networkStatus); IP != nil { - // Tell CNI which IP address we want. - c.requestedIP = IP - } + // container with the same networks settings as during checkpointing. + aliases, err := c.runtime.state.GetAllNetworkAliases(c) + if err != nil { + return err } - if !options.IgnoreStaticMAC { - if MAC := metadata.GetMACFromNetworkStatus(networkStatus); MAC != nil { - // Tell CNI which MAC address we want. - c.requestedMAC = MAC + netOpts := make(map[string]types.PerNetworkOptions, len(netStatus)) + for network, status := range netStatus { + perNetOpts := types.PerNetworkOptions{} + for name, netInt := range status.Interfaces { + perNetOpts = types.PerNetworkOptions{ + InterfaceName: name, + Aliases: aliases[network], + } + if !options.IgnoreStaticMAC { + perNetOpts.StaticMAC = netInt.MacAddress + } + if !options.IgnoreStaticIP { + for _, netAddress := range netInt.Networks { + perNetOpts.StaticIPs = append(perNetOpts.StaticIPs, netAddress.Subnet.IP) + } + } + // Normally interfaces have a length of 1, only for some special cni configs we could get more. + // For now just use the first interface to get the ips this should be good enough for most cases. + break + } + if perNetOpts.InterfaceName == "" { + eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(network) + if !exists { + return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, network) + } + perNetOpts.InterfaceName = eth } + netOpts[network] = perNetOpts } + c.perNetworkOpts = netOpts } defer func() { @@ -1785,9 +1812,9 @@ rootless=%d // generateResolvConf generates a containers resolv.conf func (c *Container) generateResolvConf() (string, error) { var ( - nameservers []string - cniNameServers []string - cniSearchDomains []string + nameservers []string + networkNameServers []string + networkSearchDomains []string ) resolvConf := "/etc/resolv.conf" @@ -1827,22 +1854,27 @@ func (c *Container) generateResolvConf() (string, error) { } ipv6 := false - // Check if CNI gave back and DNS servers for us to add in - cniResponse := c.state.NetworkStatus - for _, i := range cniResponse { - for _, ip := range i.IPs { - // Note: only using To16() does not work since it also returns a valid ip for ipv4 - if ip.Address.IP.To4() == nil && ip.Address.IP.To16() != nil { - ipv6 = true + // If network status is set check for ipv6 and dns namesevers + netStatus := c.getNetworkStatus() + for _, status := range netStatus { + for _, netInt := range status.Interfaces { + for _, netAddress := range netInt.Networks { + // Note: only using To16() does not work since it also returns a valid ip for ipv4 + if netAddress.Subnet.IP.To4() == nil && netAddress.Subnet.IP.To16() != nil { + ipv6 = true + } } } - if i.DNS.Nameservers != nil { - cniNameServers = append(cniNameServers, i.DNS.Nameservers...) - logrus.Debugf("adding nameserver(s) from cni response of '%q'", i.DNS.Nameservers) + + if status.DNSServerIPs != nil { + for _, nsIP := range status.DNSServerIPs { + networkNameServers = append(networkNameServers, nsIP.String()) + } + logrus.Debugf("adding nameserver(s) from network status of '%q'", status.DNSServerIPs) } - if i.DNS.Search != nil { - cniSearchDomains = append(cniSearchDomains, i.DNS.Search...) - logrus.Debugf("adding search domain(s) from cni response of '%q'", i.DNS.Search) + if status.DNSSearchDomains != nil { + networkSearchDomains = append(networkSearchDomains, status.DNSSearchDomains...) + logrus.Debugf("adding search domain(s) from network status of '%q'", status.DNSSearchDomains) } } @@ -1882,8 +1914,8 @@ func (c *Container) generateResolvConf() (string, error) { for _, server := range dnsServers { nameservers = append(nameservers, server.String()) } - case len(cniNameServers) > 0: - nameservers = append(nameservers, cniNameServers...) + case len(networkNameServers) > 0: + nameservers = append(nameservers, networkNameServers...) default: // Make a new resolv.conf nameservers = resolvconf.GetNameservers(resolv.Content) @@ -1899,11 +1931,11 @@ func (c *Container) generateResolvConf() (string, error) { } var search []string - if len(c.config.DNSSearch) > 0 || len(c.runtime.config.Containers.DNSSearches) > 0 || len(cniSearchDomains) > 0 { + if len(c.config.DNSSearch) > 0 || len(c.runtime.config.Containers.DNSSearches) > 0 || len(networkSearchDomains) > 0 { if !util.StringInSlice(".", c.config.DNSSearch) { search = c.runtime.config.Containers.DNSSearches search = append(search, c.config.DNSSearch...) - search = append(search, cniSearchDomains...) + search = append(search, networkSearchDomains...) } } else { search = resolvconf.GetSearchDomains(resolv.Content) @@ -2019,20 +2051,22 @@ func (c *Container) getHosts() string { // Add gateway entry var depCtr *Container + netStatus := c.getNetworkStatus() if c.config.NetNsCtr != "" { // ignoring the error because there isn't anything to do depCtr, _ = c.getRootNetNsDepCtr() - } else if len(c.state.NetworkStatus) != 0 { + } else if len(netStatus) != 0 { depCtr = c - } else { - depCtr = nil } if depCtr != nil { - for _, pluginResultsRaw := range depCtr.state.NetworkStatus { - pluginResult, _ := cnitypes.GetResult(pluginResultsRaw) - for _, ip := range pluginResult.IPs { - hosts += fmt.Sprintf("%s host.containers.internal\n", ip.Gateway) + for _, status := range depCtr.getNetworkStatus() { + for _, netInt := range status.Interfaces { + for _, netAddress := range netInt.Networks { + if netAddress.Gateway != nil { + hosts += fmt.Sprintf("%s host.containers.internal\n", netAddress.Gateway.String()) + } + } } } } else if c.config.NetMode.IsSlirp4netns() { diff --git a/libpod/info.go b/libpod/info.go index 2eba4bbff..122a6b587 100644 --- a/libpod/info.go +++ b/libpod/info.go @@ -18,7 +18,7 @@ import ( "github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/linkmode" - "github.com/containers/podman/v3/libpod/network" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/storage" @@ -74,7 +74,7 @@ func (r *Runtime) info() (*define.Info, error) { } info.Plugins.Volume = volumePlugins // TODO move this into the new network interface - info.Plugins.Network = []string{network.BridgeNetworkDriver, network.MacVLANNetworkDriver} + info.Plugins.Network = []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver} info.Plugins.Log = logDrivers info.Registries = registries diff --git a/libpod/network/cni/run.go b/libpod/network/cni/run.go index 14634262c..b69953c4b 100644 --- a/libpod/network/cni/run.go +++ b/libpod/network/cni/run.go @@ -114,7 +114,7 @@ func (n *cniNetwork) Setup(namespacePath string, options types.SetupOptions) (ma } logrus.Debugf("cni result for container %s network %s: %v", options.ContainerID, name, cnires) var status types.StatusBlock - status, retErr = cniResultToStatus(cnires) + status, retErr = CNIResultToStatus(cnires) if retErr != nil { return nil, retErr } @@ -123,8 +123,9 @@ func (n *cniNetwork) Setup(namespacePath string, options types.SetupOptions) (ma return results, nil } -// cniResultToStatus convert the cni result to status block -func cniResultToStatus(cniResult *current.Result) (types.StatusBlock, error) { +// CNIResultToStatus convert the cni result to status block +// nolint:golint +func CNIResultToStatus(cniResult *current.Result) (types.StatusBlock, error) { result := types.StatusBlock{} nameservers := make([]net.IP, 0, len(cniResult.DNS.Nameservers)) for _, nameserver := range cniResult.DNS.Nameservers { diff --git a/libpod/network/config.go b/libpod/network/config.go deleted file mode 100644 index 9a3bc4763..000000000 --- a/libpod/network/config.go +++ /dev/null @@ -1,159 +0,0 @@ -package network - -import ( - "encoding/json" - "net" - - "github.com/containers/storage/pkg/lockfile" -) - -// TODO once the containers.conf file stuff is worked out, this should be modified -// to honor defines in the containers.conf as well as overrides? - -const ( - // CNIConfigDir is the path where CNI config files exist - CNIConfigDir = "/etc/cni/net.d" - // CNIDeviceName is the default network device name and in - // reality should have an int appended to it (cni-podman4) - CNIDeviceName = "cni-podman" - // DefaultPodmanDomainName is used for the dnsname plugin to define - // a localized domain name for a created network - DefaultPodmanDomainName = "dns.podman" - // LockFileName is used for obtaining a lock and is appended - // to libpod's tmpdir in practice - LockFileName = "cni.lock" -) - -// CNILock is for preventing name collision and -// unpredictable results when doing some CNI operations. -type CNILock struct { - lockfile.Locker -} - -// GetDefaultPodmanNetwork outputs the default network for podman -func GetDefaultPodmanNetwork() (*net.IPNet, error) { - _, n, err := net.ParseCIDR("10.88.1.0/24") - return n, err -} - -// CNIPlugins is a way of marshalling a CNI network configuration to disk -type CNIPlugins interface { - Bytes() ([]byte, error) -} - -// HostLocalBridge describes a configuration for a bridge plugin -// https://github.com/containernetworking/plugins/tree/master/plugins/main/bridge#network-configuration-reference -type HostLocalBridge struct { - PluginType string `json:"type"` - BrName string `json:"bridge,omitempty"` - IsGW bool `json:"isGateway"` - IsDefaultGW bool `json:"isDefaultGateway,omitempty"` - ForceAddress bool `json:"forceAddress,omitempty"` - IPMasq bool `json:"ipMasq,omitempty"` - MTU int `json:"mtu,omitempty"` - HairpinMode bool `json:"hairpinMode,omitempty"` - PromiscMode bool `json:"promiscMode,omitempty"` - Vlan int `json:"vlan,omitempty"` - IPAM IPAMConfig `json:"ipam"` -} - -// Bytes outputs []byte -func (h *HostLocalBridge) Bytes() ([]byte, error) { - return json.MarshalIndent(h, "", "\t") -} - -// IPAMConfig describes an IPAM configuration -// https://github.com/containernetworking/plugins/tree/master/plugins/ipam/host-local#network-configuration-reference -type IPAMConfig struct { - PluginType string `json:"type"` - Routes []IPAMRoute `json:"routes,omitempty"` - ResolveConf string `json:"resolveConf,omitempty"` - DataDir string `json:"dataDir,omitempty"` - Ranges [][]IPAMLocalHostRangeConf `json:"ranges,omitempty"` -} - -// IPAMLocalHostRangeConf describes the new style IPAM ranges -type IPAMLocalHostRangeConf struct { - Subnet string `json:"subnet"` - RangeStart string `json:"rangeStart,omitempty"` - RangeEnd string `json:"rangeEnd,omitempty"` - Gateway string `json:"gateway,omitempty"` -} - -// Bytes outputs the configuration as []byte -func (i IPAMConfig) Bytes() ([]byte, error) { - return json.MarshalIndent(i, "", "\t") -} - -// IPAMRoute describes a route in an ipam config -type IPAMRoute struct { - Dest string `json:"dst"` -} - -// PortMapConfig describes the default portmapping config -type PortMapConfig struct { - PluginType string `json:"type"` - Capabilities map[string]bool `json:"capabilities"` -} - -// Bytes outputs the configuration as []byte -func (p PortMapConfig) Bytes() ([]byte, error) { - return json.MarshalIndent(p, "", "\t") -} - -// MacVLANConfig describes the macvlan config -type MacVLANConfig struct { - PluginType string `json:"type"` - Master string `json:"master"` - IPAM IPAMConfig `json:"ipam"` - MTU int `json:"mtu,omitempty"` -} - -// Bytes outputs the configuration as []byte -func (p MacVLANConfig) Bytes() ([]byte, error) { - return json.MarshalIndent(p, "", "\t") -} - -// FirewallConfig describes the firewall plugin -type FirewallConfig struct { - PluginType string `json:"type"` - Backend string `json:"backend"` -} - -// Bytes outputs the configuration as []byte -func (f FirewallConfig) Bytes() ([]byte, error) { - return json.MarshalIndent(f, "", "\t") -} - -// TuningConfig describes the tuning plugin -type TuningConfig struct { - PluginType string `json:"type"` -} - -// Bytes outputs the configuration as []byte -func (f TuningConfig) Bytes() ([]byte, error) { - return json.MarshalIndent(f, "", "\t") -} - -// DNSNameConfig describes the dns container name resolution plugin config -type DNSNameConfig struct { - PluginType string `json:"type"` - DomainName string `json:"domainName"` - Capabilities map[string]bool `json:"capabilities"` -} - -// PodmanMachineConfig enables port handling on the host OS -type PodmanMachineConfig struct { - PluginType string `json:"type"` - Capabilities map[string]bool `json:"capabilities"` -} - -// Bytes outputs the configuration as []byte -func (d DNSNameConfig) Bytes() ([]byte, error) { - return json.MarshalIndent(d, "", "\t") -} - -// Bytes outputs the configuration as []byte -func (p PodmanMachineConfig) Bytes() ([]byte, error) { - return json.MarshalIndent(p, "", "\t") -} diff --git a/libpod/network/create.go b/libpod/network/create.go deleted file mode 100644 index aca8150b5..000000000 --- a/libpod/network/create.go +++ /dev/null @@ -1,310 +0,0 @@ -package network - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strconv" - - "github.com/containernetworking/cni/pkg/version" - "github.com/containers/common/pkg/config" - "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/util" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// Create the CNI network -func Create(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (*entities.NetworkCreateReport, error) { - var fileName string - if err := isSupportedDriver(options.Driver); err != nil { - return nil, err - } - // Acquire a lock for CNI - l, err := acquireCNILock(runtimeConfig) - if err != nil { - return nil, err - } - defer l.releaseCNILock() - if len(options.MacVLAN) > 0 || options.Driver == MacVLANNetworkDriver { - fileName, err = createMacVLAN(name, options, runtimeConfig) - } else { - fileName, err = createBridge(name, options, runtimeConfig) - } - if err != nil { - return nil, err - } - return &entities.NetworkCreateReport{Filename: fileName}, nil -} - -// validateBridgeOptions validate the bridge networking options -func validateBridgeOptions(options entities.NetworkCreateOptions) error { - subnet := &options.Subnet - ipRange := &options.Range - gateway := options.Gateway - // if IPv6 is set an IPv6 subnet MUST be specified - if options.IPv6 && ((subnet.IP == nil) || (subnet.IP != nil && !IsIPv6(subnet.IP))) { - return errors.Errorf("ipv6 option requires an IPv6 --subnet to be provided") - } - // range and gateway depend on subnet - if subnet.IP == nil && (ipRange.IP != nil || gateway != nil) { - return errors.Errorf("every ip-range or gateway must have a corresponding subnet") - } - - // if a range is given, we need to ensure it is "in" the network range. - if ipRange.IP != nil { - firstIP, err := FirstIPInSubnet(ipRange) - if err != nil { - return errors.Wrapf(err, "failed to get first IP address from ip-range") - } - lastIP, err := LastIPInSubnet(ipRange) - if err != nil { - return errors.Wrapf(err, "failed to get last IP address from ip-range") - } - if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) { - return errors.Errorf("the ip range %s does not fall within the subnet range %s", ipRange.String(), subnet.String()) - } - } - - // if network is provided and if gateway is provided, make sure it is "in" network - if gateway != nil && !subnet.Contains(gateway) { - return errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String()) - } - - return nil -} - -// parseMTU parses the mtu option -func parseMTU(mtu string) (int, error) { - if mtu == "" { - return 0, nil // default - } - m, err := strconv.Atoi(mtu) - if err != nil { - return 0, err - } - if m < 0 { - return 0, errors.Errorf("the value %d for mtu is less than zero", m) - } - return m, nil -} - -// parseVlan parses the vlan option -func parseVlan(vlan string) (int, error) { - if vlan == "" { - return 0, nil // default - } - return strconv.Atoi(vlan) -} - -// createBridge creates a CNI network -func createBridge(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) { - var ( - ipamRanges [][]IPAMLocalHostRangeConf - err error - routes []IPAMRoute - ) - isGateway := true - ipMasq := true - - // validate options - if err := validateBridgeOptions(options); err != nil { - return "", err - } - - // For compatibility with the docker implementation: - // if IPv6 is enabled (it really means dual-stack) then an IPv6 subnet has to be provided, and one free network is allocated for IPv4 - // if IPv6 is not specified the subnet may be specified and can be either IPv4 or IPv6 (podman, unlike docker, allows IPv6 only networks) - // If not subnet is specified an IPv4 subnet will be allocated - subnet := &options.Subnet - ipRange := &options.Range - gateway := options.Gateway - if subnet.IP != nil { - // if network is provided, does it conflict with existing CNI or live networks - err = ValidateUserNetworkIsAvailable(runtimeConfig, subnet) - if err != nil { - return "", err - } - // obtain CNI subnet default route - defaultRoute, err := NewIPAMDefaultRoute(IsIPv6(subnet.IP)) - if err != nil { - return "", err - } - routes = append(routes, defaultRoute) - // obtain CNI range - ipamRange, err := NewIPAMLocalHostRange(subnet, ipRange, gateway) - if err != nil { - return "", err - } - ipamRanges = append(ipamRanges, ipamRange) - } - // if no network is provided or IPv6 flag used, figure out the IPv4 network - if options.IPv6 || len(routes) == 0 { - subnetV4, err := GetFreeNetwork(runtimeConfig) - if err != nil { - return "", err - } - // obtain IPv4 default route - defaultRoute, err := NewIPAMDefaultRoute(false) - if err != nil { - return "", err - } - routes = append(routes, defaultRoute) - // the CNI bridge plugin does not need to set - // the range or gateway options explicitly - ipamRange, err := NewIPAMLocalHostRange(subnetV4, nil, nil) - if err != nil { - return "", err - } - ipamRanges = append(ipamRanges, ipamRange) - } - - // create CNI config - ipamConfig, err := NewIPAMHostLocalConf(routes, ipamRanges) - if err != nil { - return "", err - } - - if options.Internal { - isGateway = false - ipMasq = false - } - - var mtu int - var vlan int - for k, v := range options.Options { - var err error - switch k { - case "mtu": - mtu, err = parseMTU(v) - if err != nil { - return "", err - } - - case "vlan": - vlan, err = parseVlan(v) - if err != nil { - return "", err - } - - default: - return "", errors.Errorf("unsupported option %s", k) - } - } - - // obtain host bridge name - bridgeDeviceName, err := GetFreeDeviceName(runtimeConfig) - if err != nil { - return "", err - } - - if len(name) > 0 { - netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig) - if err != nil { - return "", err - } - if util.StringInSlice(name, netNames) { - return "", errors.Errorf("the network name %s is already used", name) - } - } else { - // If no name is given, we give the name of the bridge device - name = bridgeDeviceName - } - - // create CNI plugin configuration - ncList := NewNcList(name, version.Current(), options.Labels) - var plugins []CNIPlugins - // TODO need to iron out the role of isDefaultGW and IPMasq - bridge := NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, mtu, vlan, ipamConfig) - plugins = append(plugins, bridge) - plugins = append(plugins, NewPortMapPlugin()) - plugins = append(plugins, NewFirewallPlugin()) - plugins = append(plugins, NewTuningPlugin()) - // if we find the dnsname plugin we add configuration for it - if HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) && !options.DisableDNS { - if options.Internal { - logrus.Warnf("dnsname and --internal networks are incompatible. dnsname plugin not configured for network %s", name) - } else { - // Note: in the future we might like to allow for dynamic domain names - plugins = append(plugins, NewDNSNamePlugin(DefaultPodmanDomainName)) - } - } - // Add the podman-machine CNI plugin if we are in a machine - if runtimeConfig.MachineEnabled() { // check if we are in a machine vm - plugins = append(plugins, NewPodmanMachinePlugin()) - } - ncList["plugins"] = plugins - b, err := json.MarshalIndent(ncList, "", " ") - if err != nil { - return "", err - } - if err := os.MkdirAll(GetCNIConfDir(runtimeConfig), 0755); err != nil { - return "", err - } - cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name)) - err = ioutil.WriteFile(cniPathName, b, 0644) - return cniPathName, err -} - -func createMacVLAN(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) { - var ( - mtu int - plugins []CNIPlugins - ) - liveNetNames, err := GetLiveNetworkNames() - if err != nil { - return "", err - } - - // The parent can be defined with --macvlan or as an option (-o parent:device) - parentNetworkDevice := options.MacVLAN - if len(parentNetworkDevice) < 1 { - if parent, ok := options.Options["parent"]; ok { - parentNetworkDevice = parent - } - } - - // Make sure the host-device exists if provided - if len(parentNetworkDevice) > 0 && !util.StringInSlice(parentNetworkDevice, liveNetNames) { - return "", errors.Errorf("failed to find network interface %q", parentNetworkDevice) - } - if len(name) > 0 { - netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig) - if err != nil { - return "", err - } - if util.StringInSlice(name, netNames) { - return "", errors.Errorf("the network name %s is already used", name) - } - } else { - name, err = GetFreeDeviceName(runtimeConfig) - if err != nil { - return "", err - } - } - ncList := NewNcList(name, version.Current(), options.Labels) - if val, ok := options.Options["mtu"]; ok { - intVal, err := strconv.Atoi(val) - if err != nil { - return "", err - } - if intVal > 0 { - mtu = intVal - } - } - macvlan, err := NewMacVLANPlugin(parentNetworkDevice, options.Gateway, &options.Range, &options.Subnet, mtu) - if err != nil { - return "", err - } - plugins = append(plugins, macvlan) - ncList["plugins"] = plugins - b, err := json.MarshalIndent(ncList, "", " ") - if err != nil { - return "", err - } - cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name)) - err = ioutil.WriteFile(cniPathName, b, 0644) - return cniPathName, err -} diff --git a/libpod/network/create_test.go b/libpod/network/create_test.go deleted file mode 100644 index c3824bd91..000000000 --- a/libpod/network/create_test.go +++ /dev/null @@ -1,130 +0,0 @@ -package network - -import ( - "net" - "testing" - - "github.com/containers/podman/v3/pkg/domain/entities" -) - -func Test_validateBridgeOptions(t *testing.T) { - tests := []struct { - name string - subnet net.IPNet - ipRange net.IPNet - gateway net.IP - isIPv6 bool - wantErr bool - }{ - { - name: "IPv4 subnet only", - subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - }, - { - name: "IPv4 subnet and range", - subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)}, - }, - { - name: "IPv4 subnet and gateway", - subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - gateway: net.ParseIP("192.168.0.10"), - }, - { - name: "IPv4 subnet, range and gateway", - subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)}, - gateway: net.ParseIP("192.168.0.10"), - }, - { - name: "IPv6 subnet only", - subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - }, - { - name: "IPv6 subnet and range", - subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - ipRange: net.IPNet{IP: net.ParseIP("2001:DB8:0:0:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))}, - isIPv6: true, - }, - { - name: "IPv6 subnet and gateway", - subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - gateway: net.ParseIP("2001:DB8::2"), - isIPv6: true, - }, - { - name: "IPv6 subnet, range and gateway", - subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - ipRange: net.IPNet{IP: net.ParseIP("2001:DB8:0:0:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))}, - gateway: net.ParseIP("2001:DB8::2"), - isIPv6: true, - }, - { - name: "IPv6 subnet, range and gateway without IPv6 option (PODMAN SUPPORTS IT UNLIKE DOCKER)", - subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - ipRange: net.IPNet{IP: net.ParseIP("2001:DB8:0:0:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))}, - gateway: net.ParseIP("2001:DB8::2"), - isIPv6: false, - }, - { - name: "range provided but not subnet", - ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)}, - wantErr: true, - }, - { - name: "gateway provided but not subnet", - gateway: net.ParseIP("192.168.0.10"), - wantErr: true, - }, - { - name: "IPv4 subnet but IPv6 required", - subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)}, - gateway: net.ParseIP("192.168.0.10"), - isIPv6: true, - wantErr: true, - }, - { - name: "IPv6 required but IPv4 options used", - subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)}, - gateway: net.ParseIP("192.168.0.10"), - isIPv6: true, - wantErr: true, - }, - { - name: "IPv6 required but not subnet provided", - isIPv6: true, - wantErr: true, - }, - { - name: "range out of the subnet", - subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - ipRange: net.IPNet{IP: net.ParseIP("2001:1:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))}, - gateway: net.ParseIP("2001:DB8::2"), - isIPv6: true, - wantErr: true, - }, - { - name: "gateway out of the subnet", - subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - gateway: net.ParseIP("2001::2"), - isIPv6: true, - wantErr: true, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - options := entities.NetworkCreateOptions{ - Subnet: tt.subnet, - Range: tt.ipRange, - Gateway: tt.gateway, - IPv6: tt.isIPv6, - } - if err := validateBridgeOptions(options); (err != nil) != tt.wantErr { - t.Errorf("validateBridgeOptions() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} diff --git a/libpod/network/devices.go b/libpod/network/devices.go deleted file mode 100644 index fc9aff337..000000000 --- a/libpod/network/devices.go +++ /dev/null @@ -1,59 +0,0 @@ -package network - -import ( - "fmt" - - "github.com/containers/common/pkg/config" - "github.com/containers/podman/v3/pkg/util" - "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" -) - -// GetFreeDeviceName returns a device name that is unused; used when no network -// name is provided by user -func GetFreeDeviceName(config *config.Config) (string, error) { - var ( - deviceNum uint - deviceName string - ) - networkNames, err := GetNetworkNamesFromFileSystem(config) - if err != nil { - return "", err - } - liveNetworksNames, err := GetLiveNetworkNames() - if err != nil { - return "", err - } - bridgeNames, err := GetBridgeNamesFromFileSystem(config) - if err != nil { - return "", err - } - for { - deviceName = fmt.Sprintf("%s%d", CNIDeviceName, deviceNum) - logrus.Debugf("checking if device name %q exists in other cni networks", deviceName) - if util.StringInSlice(deviceName, networkNames) { - deviceNum++ - continue - } - logrus.Debugf("checking if device name %q exists in live networks", deviceName) - if util.StringInSlice(deviceName, liveNetworksNames) { - deviceNum++ - continue - } - logrus.Debugf("checking if device name %q already exists as a bridge name ", deviceName) - if !util.StringInSlice(deviceName, bridgeNames) { - break - } - deviceNum++ - } - return deviceName, nil -} - -// RemoveInterface removes an interface by the given name -func RemoveInterface(interfaceName string) error { - link, err := netlink.LinkByName(interfaceName) - if err != nil { - return err - } - return netlink.LinkDel(link) -} diff --git a/libpod/network/files.go b/libpod/network/files.go deleted file mode 100644 index d876113f9..000000000 --- a/libpod/network/files.go +++ /dev/null @@ -1,211 +0,0 @@ -package network - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "sort" - "strings" - - "github.com/containernetworking/cni/libcni" - "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator" - "github.com/containers/common/pkg/config" - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/pkg/network" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// ErrNoSuchNetworkInterface indicates that no network interface exists -var ErrNoSuchNetworkInterface = errors.New("unable to find interface name for network") - -// GetCNIConfDir get CNI configuration directory -func GetCNIConfDir(configArg *config.Config) string { - if len(configArg.Network.NetworkConfigDir) < 1 { - dc, err := config.DefaultConfig() - if err != nil { - // Fallback to hard-coded dir - return CNIConfigDir - } - return dc.Network.NetworkConfigDir - } - return configArg.Network.NetworkConfigDir -} - -// LoadCNIConfsFromDir loads all the CNI configurations from a dir -func LoadCNIConfsFromDir(dir string) ([]*libcni.NetworkConfigList, error) { - files, err := libcni.ConfFiles(dir, []string{".conflist"}) - if err != nil { - return nil, err - } - sort.Strings(files) - - configs := make([]*libcni.NetworkConfigList, 0, len(files)) - for _, confFile := range files { - conf, err := libcni.ConfListFromFile(confFile) - if err != nil { - return nil, errors.Wrapf(err, "in %s", confFile) - } - configs = append(configs, conf) - } - return configs, nil -} - -// GetCNIConfigPathByNameOrID finds a CNI network by name and -// returns its configuration file path -func GetCNIConfigPathByNameOrID(config *config.Config, name string) (string, error) { - files, err := libcni.ConfFiles(GetCNIConfDir(config), []string{".conflist"}) - if err != nil { - return "", err - } - idMatch := 0 - file := "" - for _, confFile := range files { - conf, err := libcni.ConfListFromFile(confFile) - if err != nil { - return "", errors.Wrapf(err, "in %s", confFile) - } - if conf.Name == name { - return confFile, nil - } - if strings.HasPrefix(network.GetNetworkID(conf.Name), name) { - idMatch++ - file = confFile - } - } - if idMatch == 1 { - return file, nil - } - if idMatch > 1 { - return "", errors.Errorf("more than one result for network ID %s", name) - } - return "", errors.Wrap(define.ErrNoSuchNetwork, fmt.Sprintf("unable to find network configuration for %s", name)) -} - -// ReadRawCNIConfByNameOrID reads the raw CNI configuration for a CNI -// network by name -func ReadRawCNIConfByNameOrID(config *config.Config, name string) ([]byte, error) { - confFile, err := GetCNIConfigPathByNameOrID(config, name) - if err != nil { - return nil, err - } - b, err := ioutil.ReadFile(confFile) - return b, err -} - -// GetNetworkLabels returns a list of labels as a string -func GetNetworkLabels(list *libcni.NetworkConfigList) NcLabels { - cniJSON := make(map[string]interface{}) - err := json.Unmarshal(list.Bytes, &cniJSON) - if err != nil { - logrus.Errorf("failed to unmarshal network config %v %v", cniJSON["name"], err) - return nil - } - if args, ok := cniJSON["args"]; ok { - if key, ok := args.(map[string]interface{}); ok { - if labels, ok := key[PodmanLabelKey]; ok { - if labels, ok := labels.(map[string]interface{}); ok { - result := make(NcLabels, len(labels)) - for k, v := range labels { - if v, ok := v.(string); ok { - result[k] = v - } else { - logrus.Errorf("network config %v invalid label value type %T should be string", cniJSON["name"], labels) - } - } - return result - } - logrus.Errorf("network config %v invalid label type %T should be map[string]string", cniJSON["name"], labels) - } - } - } - return nil -} - -// GetNetworksFromFilesystem gets all the networks from the cni configuration -// files -func GetNetworksFromFilesystem(config *config.Config) ([]*allocator.Net, error) { - var cniNetworks []*allocator.Net - - networks, err := LoadCNIConfsFromDir(GetCNIConfDir(config)) - if err != nil { - return nil, err - } - for _, n := range networks { - for _, cniplugin := range n.Plugins { - if cniplugin.Network.Type == "bridge" { - ipamConf := allocator.Net{} - if err := json.Unmarshal(cniplugin.Bytes, &ipamConf); err != nil { - return nil, err - } - cniNetworks = append(cniNetworks, &ipamConf) - break - } - } - } - return cniNetworks, nil -} - -// GetNetworkNamesFromFileSystem gets all the names from the cni network -// configuration files -func GetNetworkNamesFromFileSystem(config *config.Config) ([]string, error) { - networks, err := LoadCNIConfsFromDir(GetCNIConfDir(config)) - if err != nil { - return nil, err - } - networkNames := []string{} - for _, n := range networks { - networkNames = append(networkNames, n.Name) - } - return networkNames, nil -} - -// GetInterfaceNameFromConfig returns the interface name for the bridge plugin -func GetInterfaceNameFromConfig(path string) (string, error) { - var name string - conf, err := libcni.ConfListFromFile(path) - if err != nil { - return "", err - } - for _, cniplugin := range conf.Plugins { - if cniplugin.Network.Type == "bridge" { - plugin := make(map[string]interface{}) - if err := json.Unmarshal(cniplugin.Bytes, &plugin); err != nil { - return "", err - } - name = plugin["bridge"].(string) - break - } - } - if len(name) == 0 { - return "", ErrNoSuchNetworkInterface - } - return name, nil -} - -// GetBridgeNamesFromFileSystem is a convenience function to get all the bridge -// names from the configured networks -func GetBridgeNamesFromFileSystem(config *config.Config) ([]string, error) { - networks, err := LoadCNIConfsFromDir(GetCNIConfDir(config)) - if err != nil { - return nil, err - } - - bridgeNames := []string{} - for _, n := range networks { - var name string - // iterate network conflists - for _, cniplugin := range n.Plugins { - // iterate plugins - if cniplugin.Network.Type == "bridge" { - plugin := make(map[string]interface{}) - if err := json.Unmarshal(cniplugin.Bytes, &plugin); err != nil { - continue - } - name = plugin["bridge"].(string) - } - } - bridgeNames = append(bridgeNames, name) - } - return bridgeNames, nil -} diff --git a/libpod/network/ip.go b/libpod/network/ip.go deleted file mode 100644 index ba93a0d05..000000000 --- a/libpod/network/ip.go +++ /dev/null @@ -1,19 +0,0 @@ -package network - -import ( - "net" - - "github.com/containernetworking/plugins/pkg/ip" -) - -// CalcGatewayIP takes a network and returns the first IP in it. -func CalcGatewayIP(ipn *net.IPNet) net.IP { - // taken from cni bridge plugin as it is not exported - nid := ipn.IP.Mask(ipn.Mask) - return ip.NextIP(nid) -} - -// IsIPv6 returns if netIP is IPv6. -func IsIPv6(netIP net.IP) bool { - return netIP != nil && netIP.To4() == nil -} diff --git a/libpod/network/lock.go b/libpod/network/lock.go deleted file mode 100644 index 037f41efa..000000000 --- a/libpod/network/lock.go +++ /dev/null @@ -1,35 +0,0 @@ -package network - -import ( - "os" - "path/filepath" - - "github.com/containers/common/pkg/config" - "github.com/containers/storage" -) - -// acquireCNILock gets a lock that should be used in create and -// delete cases to avoid unwanted collisions in network names. -// TODO this uses a file lock and should be converted to shared memory -// when we have a more general shared memory lock in libpod -func acquireCNILock(config *config.Config) (*CNILock, error) { - cniDir := GetCNIConfDir(config) - err := os.MkdirAll(cniDir, 0755) - if err != nil { - return nil, err - } - l, err := storage.GetLockfile(filepath.Join(cniDir, LockFileName)) - if err != nil { - return nil, err - } - l.Lock() - cnilock := CNILock{ - Locker: l, - } - return &cnilock, nil -} - -// ReleaseCNILock unlocks the previously held lock -func (l *CNILock) releaseCNILock() { - l.Unlock() -} diff --git a/libpod/network/netconflist.go b/libpod/network/netconflist.go deleted file mode 100644 index d6c33740e..000000000 --- a/libpod/network/netconflist.go +++ /dev/null @@ -1,312 +0,0 @@ -package network - -import ( - "net" - "os" - "path/filepath" - "strings" - "syscall" - "time" - - "github.com/containernetworking/cni/libcni" - "github.com/containers/common/pkg/config" - "github.com/containers/podman/v3/pkg/network" - "github.com/containers/podman/v3/pkg/util" - "github.com/pkg/errors" -) - -const ( - defaultIPv4Route = "0.0.0.0/0" - defaultIPv6Route = "::/0" -) - -// NcList describes a generic map -type NcList map[string]interface{} - -// NcArgs describes the cni args field -type NcArgs map[string]NcLabels - -// NcLabels describes the label map -type NcLabels map[string]string - -// PodmanLabelKey key used to store the podman network label in a cni config -const PodmanLabelKey = "podman_labels" - -// NewNcList creates a generic map of values with string -// keys and adds in version and network name -func NewNcList(name, version string, labels NcLabels) NcList { - n := NcList{} - n["cniVersion"] = version - n["name"] = name - if len(labels) > 0 { - n["args"] = NcArgs{PodmanLabelKey: labels} - } - return n -} - -// NewHostLocalBridge creates a new LocalBridge for host-local -func NewHostLocalBridge(name string, isGateWay, isDefaultGW, ipMasq bool, mtu int, vlan int, ipamConf IPAMConfig) *HostLocalBridge { - hostLocalBridge := HostLocalBridge{ - PluginType: "bridge", - BrName: name, - IPMasq: ipMasq, - MTU: mtu, - HairpinMode: true, - Vlan: vlan, - IPAM: ipamConf, - } - if isGateWay { - hostLocalBridge.IsGW = true - } - if isDefaultGW { - hostLocalBridge.IsDefaultGW = true - } - return &hostLocalBridge -} - -// NewIPAMHostLocalConf creates a new IPAMHostLocal configuration -func NewIPAMHostLocalConf(routes []IPAMRoute, ipamRanges [][]IPAMLocalHostRangeConf) (IPAMConfig, error) { - ipamConf := IPAMConfig{ - PluginType: "host-local", - Routes: routes, - // Possible future support ? Leaving for clues - //ResolveConf: "", - //DataDir: "" - } - - ipamConf.Ranges = ipamRanges - return ipamConf, nil -} - -// NewIPAMLocalHostRange create a new IPAM range -func NewIPAMLocalHostRange(subnet *net.IPNet, ipRange *net.IPNet, gw net.IP) ([]IPAMLocalHostRangeConf, error) { //nolint:interfacer - var ranges []IPAMLocalHostRangeConf - hostRange := IPAMLocalHostRangeConf{ - Subnet: subnet.String(), - } - // an user provided a range, we add it here - if ipRange != nil && ipRange.IP != nil { - first, err := FirstIPInSubnet(ipRange) - if err != nil { - return nil, err - } - last, err := LastIPInSubnet(ipRange) - if err != nil { - return nil, err - } - hostRange.RangeStart = first.String() - hostRange.RangeEnd = last.String() - } - if gw != nil { - hostRange.Gateway = gw.String() - } else { - // Add first ip in subnet as gateway. It is not required - // by cni but should be included because of network inspect. - hostRange.Gateway = CalcGatewayIP(subnet).String() - } - ranges = append(ranges, hostRange) - return ranges, nil -} - -// NewIPAMRoute creates a new IPAM route configuration -func NewIPAMRoute(r *net.IPNet) IPAMRoute { //nolint:interfacer - return IPAMRoute{Dest: r.String()} -} - -// NewIPAMDefaultRoute creates a new IPAMDefault route of -// 0.0.0.0/0 for IPv4 or ::/0 for IPv6 -func NewIPAMDefaultRoute(isIPv6 bool) (IPAMRoute, error) { - route := defaultIPv4Route - if isIPv6 { - route = defaultIPv6Route - } - _, n, err := net.ParseCIDR(route) - if err != nil { - return IPAMRoute{}, err - } - return NewIPAMRoute(n), nil -} - -// NewPortMapPlugin creates a predefined, default portmapping -// configuration -func NewPortMapPlugin() PortMapConfig { - caps := make(map[string]bool) - caps["portMappings"] = true - p := PortMapConfig{ - PluginType: "portmap", - Capabilities: caps, - } - return p -} - -// NewFirewallPlugin creates a generic firewall plugin -func NewFirewallPlugin() FirewallConfig { - return FirewallConfig{ - PluginType: "firewall", - } -} - -// NewTuningPlugin creates a generic tuning section -func NewTuningPlugin() TuningConfig { - return TuningConfig{ - PluginType: "tuning", - } -} - -// NewDNSNamePlugin creates the dnsname config with a given -// domainname -func NewDNSNamePlugin(domainName string) DNSNameConfig { - caps := make(map[string]bool, 1) - caps["aliases"] = true - return DNSNameConfig{ - PluginType: "dnsname", - DomainName: domainName, - Capabilities: caps, - } -} - -// HasDNSNamePlugin looks to see if the dnsname cni plugin is present -func HasDNSNamePlugin(paths []string) bool { - for _, p := range paths { - if _, err := os.Stat(filepath.Join(p, "dnsname")); err == nil { - return true - } - } - return false -} - -// NewMacVLANPlugin creates a macvlanconfig with a given device name -func NewMacVLANPlugin(device string, gateway net.IP, ipRange *net.IPNet, subnet *net.IPNet, mtu int) (MacVLANConfig, error) { - i := IPAMConfig{PluginType: "dhcp"} - if gateway != nil || - (ipRange != nil && ipRange.IP != nil && ipRange.Mask != nil) || - (subnet != nil && subnet.IP != nil && subnet.Mask != nil) { - ipam, err := NewIPAMLocalHostRange(subnet, ipRange, gateway) - if err != nil { - return MacVLANConfig{}, err - } - ranges := make([][]IPAMLocalHostRangeConf, 0) - ranges = append(ranges, ipam) - i.Ranges = ranges - route, err := NewIPAMDefaultRoute(IsIPv6(subnet.IP)) - if err != nil { - return MacVLANConfig{}, err - } - i.Routes = []IPAMRoute{route} - i.PluginType = "host-local" - } - - m := MacVLANConfig{ - PluginType: "macvlan", - IPAM: i, - } - if mtu > 0 { - m.MTU = mtu - } - // CNI is supposed to use the default route if a - // parent device is not provided - if len(device) > 0 { - m.Master = device - } - return m, nil -} - -// IfPassesFilter filters NetworkListReport and returns true if the filter match the given config -func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]string) (bool, error) { - result := true - for key, filterValues := range filters { - result = false - switch strings.ToLower(key) { - case "name": - // matches one name, regex allowed - result = util.StringMatchRegexSlice(netconf.Name, filterValues) - - case "plugin": - // match one plugin - plugins := network.GetCNIPlugins(netconf) - for _, val := range filterValues { - if strings.Contains(plugins, val) { - result = true - break - } - } - - case "label": - // matches all labels - result = util.MatchLabelFilters(filterValues, GetNetworkLabels(netconf)) - - case "driver": - // matches only for the DefaultNetworkDriver - for _, filterValue := range filterValues { - plugins := network.GetCNIPlugins(netconf) - if filterValue == DefaultNetworkDriver && - strings.Contains(plugins, DefaultNetworkDriver) { - result = true - } - } - - case "id": - // matches part of one id - for _, filterValue := range filterValues { - if strings.Contains(network.GetNetworkID(netconf.Name), filterValue) { - result = true - break - } - } - - // TODO: add dangling filter - - default: - return false, errors.Errorf("invalid filter %q", key) - } - } - return result, nil -} - -// IfPassesPruneFilter filters NetworkListReport and returns true if the prune filter match the given config -func IfPassesPruneFilter(config *config.Config, netconf *libcni.NetworkConfigList, f map[string][]string) (bool, error) { - for key, filterValues := range f { - switch strings.ToLower(key) { - case "label": - return util.MatchLabelFilters(filterValues, GetNetworkLabels(netconf)), nil - case "until": - until, err := util.ComputeUntilTimestamp(filterValues) - if err != nil { - return false, err - } - created, err := getCreatedTimestamp(config, netconf) - if err != nil { - return false, err - } - if created.Before(until) { - return true, nil - } - default: - return false, errors.Errorf("invalid filter %q", key) - } - } - return false, nil -} - -func getCreatedTimestamp(config *config.Config, netconf *libcni.NetworkConfigList) (*time.Time, error) { - networkConfigPath, err := GetCNIConfigPathByNameOrID(config, netconf.Name) - if err != nil { - return nil, err - } - f, err := os.Stat(networkConfigPath) - if err != nil { - return nil, err - } - stat := f.Sys().(*syscall.Stat_t) - created := time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)) // nolint: unconvert - return &created, nil -} - -func NewPodmanMachinePlugin() PodmanMachineConfig { - caps := make(map[string]bool, 1) - caps["portMappings"] = true - return PodmanMachineConfig{ - PluginType: "podman-machine", - Capabilities: caps, - } -} diff --git a/libpod/network/netconflist_test.go b/libpod/network/netconflist_test.go deleted file mode 100644 index 161764ed9..000000000 --- a/libpod/network/netconflist_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package network - -import ( - "net" - "reflect" - "testing" -) - -func TestNewIPAMDefaultRoute(t *testing.T) { - tests := []struct { - name string - isIPv6 bool - want IPAMRoute - }{ - { - name: "IPv4 default route", - isIPv6: false, - want: IPAMRoute{defaultIPv4Route}, - }, - { - name: "IPv6 default route", - isIPv6: true, - want: IPAMRoute{defaultIPv6Route}, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - got, err := NewIPAMDefaultRoute(tt.isIPv6) - if err != nil { - t.Errorf("no error expected: %v", err) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewIPAMDefaultRoute() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestNewIPAMLocalHostRange(t *testing.T) { - tests := []struct { - name string - subnet *net.IPNet - ipRange *net.IPNet - gw net.IP - want []IPAMLocalHostRangeConf - }{ - { - name: "IPv4 subnet", - subnet: &net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - want: []IPAMLocalHostRangeConf{ - { - Subnet: "192.168.0.0/24", - Gateway: "192.168.0.1", - }, - }, - }, - { - name: "IPv4 subnet, range and gateway", - subnet: &net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - ipRange: &net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)}, - gw: net.ParseIP("192.168.0.10"), - want: []IPAMLocalHostRangeConf{ - { - Subnet: "192.168.0.0/24", - RangeStart: "192.168.0.129", - RangeEnd: "192.168.0.255", - Gateway: "192.168.0.10", - }, - }, - }, - { - name: "IPv6 subnet", - subnet: &net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - want: []IPAMLocalHostRangeConf{ - { - Subnet: "2001:db8::/48", - Gateway: "2001:db8::1", - }, - }, - }, - { - name: "IPv6 subnet, range and gateway", - subnet: &net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - ipRange: &net.IPNet{IP: net.ParseIP("2001:DB8:1:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))}, - gw: net.ParseIP("2001:DB8::2"), - want: []IPAMLocalHostRangeConf{ - { - Subnet: "2001:db8::/48", - RangeStart: "2001:db8:1:1::1", - RangeEnd: "2001:db8:1:1:ffff:ffff:ffff:ffff", - Gateway: "2001:db8::2", - }, - }, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - got, err := NewIPAMLocalHostRange(tt.subnet, tt.ipRange, tt.gw) - if err != nil { - t.Errorf("no error expected: %v", err) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewIPAMLocalHostRange() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/libpod/network/network.go b/libpod/network/network.go deleted file mode 100644 index 3b81ce776..000000000 --- a/libpod/network/network.go +++ /dev/null @@ -1,288 +0,0 @@ -package network - -import ( - "encoding/json" - "net" - "os" - - "github.com/containernetworking/cni/libcni" - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator" - "github.com/containers/common/pkg/config" - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/rootless" - "github.com/containers/podman/v3/pkg/util" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -var ( - // BridgeNetworkDriver defines the bridge cni driver - BridgeNetworkDriver = "bridge" - // DefaultNetworkDriver is the default network type used - DefaultNetworkDriver = BridgeNetworkDriver - // MacVLANNetworkDriver defines the macvlan cni driver - MacVLANNetworkDriver = "macvlan" -) - -// SupportedNetworkDrivers describes the list of supported drivers -var SupportedNetworkDrivers = []string{BridgeNetworkDriver, MacVLANNetworkDriver} - -// isSupportedDriver checks if the user provided driver is supported -func isSupportedDriver(driver string) error { - if util.StringInSlice(driver, SupportedNetworkDrivers) { - return nil - } - return errors.Errorf("driver '%s' is not supported", driver) -} - -// GetLiveNetworks returns a slice of networks representing what the system -// has defined as network interfaces -func GetLiveNetworks() ([]*net.IPNet, error) { - addrs, err := net.InterfaceAddrs() - if err != nil { - return nil, err - } - nets := make([]*net.IPNet, 0, len(addrs)) - for _, address := range addrs { - _, n, err := net.ParseCIDR(address.String()) - if err != nil { - return nil, err - } - nets = append(nets, n) - } - return nets, nil -} - -// GetLiveNetworkNames returns a list of network interfaces on the system -func GetLiveNetworkNames() ([]string, error) { - liveInterfaces, err := net.Interfaces() - if err != nil { - return nil, err - } - interfaceNames := make([]string, 0, len(liveInterfaces)) - for _, i := range liveInterfaces { - interfaceNames = append(interfaceNames, i.Name) - } - return interfaceNames, nil -} - -// GetFreeNetwork looks for a free network according to existing cni configuration -// files and network interfaces. -func GetFreeNetwork(config *config.Config) (*net.IPNet, error) { - networks, err := GetNetworksFromFilesystem(config) - if err != nil { - return nil, err - } - liveNetworks, err := GetLiveNetworks() - if err != nil { - return nil, err - } - nextNetwork, err := GetDefaultPodmanNetwork() - if err != nil { - return nil, err - } - logrus.Debugf("default network is %s", nextNetwork.String()) - for { - newNetwork, err := NextSubnet(nextNetwork) - if err != nil { - return nil, err - } - logrus.Debugf("checking if network %s intersects with other cni networks", nextNetwork.String()) - if intersectsConfig, _ := networkIntersectsWithNetworks(newNetwork, allocatorToIPNets(networks)); intersectsConfig { - logrus.Debugf("network %s is already being used by a cni configuration", nextNetwork.String()) - nextNetwork = newNetwork - continue - } - logrus.Debugf("checking if network %s intersects with any network interfaces", nextNetwork.String()) - if intersectsLive, _ := networkIntersectsWithNetworks(newNetwork, liveNetworks); !intersectsLive { - break - } - logrus.Debugf("network %s is being used by a network interface", nextNetwork.String()) - nextNetwork = newNetwork - } - return nextNetwork, nil -} - -func allocatorToIPNets(networks []*allocator.Net) []*net.IPNet { - var nets []*net.IPNet - for _, network := range networks { - if len(network.IPAM.Ranges) > 0 { - // this is the new IPAM range style - // append each subnet from ipam the rangeset - for _, allocatorRange := range network.IPAM.Ranges { - for _, r := range allocatorRange { - nets = append(nets, newIPNetFromSubnet(r.Subnet)) - } - } - } else { - // looks like the old, deprecated style - nets = append(nets, newIPNetFromSubnet(network.IPAM.Subnet)) - } - } - return nets -} - -func newIPNetFromSubnet(subnet types.IPNet) *net.IPNet { - n := net.IPNet{ - IP: subnet.IP, - Mask: subnet.Mask, - } - return &n -} - -func networkIntersectsWithNetworks(n *net.IPNet, networklist []*net.IPNet) (bool, *net.IPNet) { - for _, nw := range networklist { - if networkIntersect(n, nw) { - return true, nw - } - } - return false, nil -} - -func networkIntersect(n1, n2 *net.IPNet) bool { - return n2.Contains(n1.IP) || n1.Contains(n2.IP) -} - -// ValidateUserNetworkIsAvailable returns via an error if a network is available -// to be used -func ValidateUserNetworkIsAvailable(config *config.Config, userNet *net.IPNet) error { - if len(userNet.IP) == 0 || len(userNet.Mask) == 0 { - return errors.Errorf("network %s's ip or mask cannot be empty", userNet.String()) - } - - ones, bit := userNet.Mask.Size() - if ones == 0 || bit == 0 { - return errors.Errorf("network %s's mask is invalid", userNet.String()) - } - - networks, err := GetNetworksFromFilesystem(config) - if err != nil { - return err - } - liveNetworks, err := GetLiveNetworks() - if err != nil { - return err - } - logrus.Debugf("checking if network %s exists in cni networks", userNet.String()) - if intersectsConfig, _ := networkIntersectsWithNetworks(userNet, allocatorToIPNets(networks)); intersectsConfig { - return errors.Errorf("network %s is already being used by a cni configuration", userNet.String()) - } - logrus.Debugf("checking if network %s exists in any network interfaces", userNet.String()) - if intersectsLive, _ := networkIntersectsWithNetworks(userNet, liveNetworks); intersectsLive { - return errors.Errorf("network %s is being used by a network interface", userNet.String()) - } - return nil -} - -// removeNetwork is removes a cni network without a lock and should only be called -// when a lock was otherwise acquired. -func removeNetwork(config *config.Config, name string) error { - cniPath, err := GetCNIConfigPathByNameOrID(config, name) - if err != nil { - return err - } - // Before we delete the configuration file, we need to make sure we can read and parse - // it to get the network interface name so we can remove that too - interfaceName, err := GetInterfaceNameFromConfig(cniPath) - if err == nil { - // Don't try to remove the network interface if we are not root - if !rootless.IsRootless() { - liveNetworkNames, err := GetLiveNetworkNames() - if err != nil { - return errors.Wrapf(err, "failed to get live network names") - } - if util.StringInSlice(interfaceName, liveNetworkNames) { - if err = RemoveInterface(interfaceName); err != nil { - // only log the error, it is not fatal - logrus.Infof("failed to remove network interface %s: %v", interfaceName, err) - } - } - } - } else if err != ErrNoSuchNetworkInterface { - // Don't error if we couldn't find the network interface name - return err - } - // Remove the configuration file - if err := os.Remove(cniPath); err != nil { - return errors.Wrap(err, "failed to remove network configuration") - } - return nil -} - -// RemoveNetwork removes a given network by name. If the network has container associated with it, that -// must be handled outside the context of this. -func RemoveNetwork(config *config.Config, name string) error { - l, err := acquireCNILock(config) - if err != nil { - return err - } - defer l.releaseCNILock() - return removeNetwork(config, name) -} - -// InspectNetwork reads a CNI config and returns its configuration -func InspectNetwork(config *config.Config, name string) (map[string]interface{}, error) { - b, err := ReadRawCNIConfByNameOrID(config, name) - if err != nil { - return nil, err - } - rawList := make(map[string]interface{}) - err = json.Unmarshal(b, &rawList) - return rawList, err -} - -// Exists says whether a given network exists or not; it meant -// specifically for restful responses so 404s can be used -func Exists(config *config.Config, name string) (bool, error) { - _, err := ReadRawCNIConfByNameOrID(config, name) - if err != nil { - if errors.Cause(err) == define.ErrNoSuchNetwork { - return false, nil - } - return false, err - } - return true, nil -} - -// PruneNetworks removes networks that are not being used and that is not the default -// network. To keep proper fencing for imports, you must provide the used networks -// to this function as a map. the key is meaningful in the map, the book is a no-op -func PruneNetworks(rtc *config.Config, usedNetworks map[string]bool) ([]*entities.NetworkPruneReport, error) { - var reports []*entities.NetworkPruneReport - lock, err := acquireCNILock(rtc) - if err != nil { - return nil, err - } - defer lock.releaseCNILock() - nets, err := GetNetworkNamesFromFileSystem(rtc) - if err != nil { - return nil, err - } - for _, n := range nets { - _, found := usedNetworks[n] - // Remove is not default network and not found in the used list - if n != rtc.Network.DefaultNetwork && !found { - reports = append(reports, &entities.NetworkPruneReport{ - Name: n, - Error: removeNetwork(rtc, n), - }) - } - } - return reports, nil -} - -// NormalizeName translates a network ID into a name. -// If the input is a name the name is returned. -func NormalizeName(config *config.Config, nameOrID string) (string, error) { - path, err := GetCNIConfigPathByNameOrID(config, nameOrID) - if err != nil { - return "", err - } - conf, err := libcni.ConfListFromFile(path) - if err != nil { - return "", err - } - return conf.Name, nil -} diff --git a/libpod/network/network_test.go b/libpod/network/network_test.go deleted file mode 100644 index 1969e792c..000000000 --- a/libpod/network/network_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package network - -import ( - "net" - "testing" -) - -func parseCIDR(n string) *net.IPNet { - _, parsedNet, _ := net.ParseCIDR(n) - return parsedNet -} - -func Test_networkIntersect(t *testing.T) { - type args struct { - n1 *net.IPNet - n2 *net.IPNet - } - tests := []struct { - name string - args args - want bool - }{ - {"16 and 24 intersects", args{n1: parseCIDR("192.168.0.0/16"), n2: parseCIDR("192.168.1.0/24")}, true}, - {"24 and 25 intersects", args{n1: parseCIDR("192.168.1.0/24"), n2: parseCIDR("192.168.1.0/25")}, true}, - {"Two 24s", args{n1: parseCIDR("192.168.1.0/24"), n2: parseCIDR("192.168.2.0/24")}, false}, - } - for _, tt := range tests { - test := tt - t.Run(tt.name, func(t *testing.T) { - if got := networkIntersect(test.args.n1, test.args.n2); got != test.want { - t.Errorf("networkIntersect() = %v, want %v", got, test.want) - } - }) - } -} diff --git a/libpod/network/subnet.go b/libpod/network/subnet.go deleted file mode 100644 index 120038e57..000000000 --- a/libpod/network/subnet.go +++ /dev/null @@ -1,78 +0,0 @@ -package network - -/* - The code in this was kindly contributed by Dan Williams(dcbw@redhat.com). Many thanks - for his contributions. -*/ - -import ( - "fmt" - "net" -) - -func incByte(subnet *net.IPNet, idx int, shift uint) error { - if idx < 0 { - return fmt.Errorf("no more subnets left") - } - if subnet.IP[idx] == 255 { - subnet.IP[idx] = 0 - return incByte(subnet, idx-1, 0) - } - subnet.IP[idx] += 1 << shift - return nil -} - -// NextSubnet returns subnet incremented by 1 -func NextSubnet(subnet *net.IPNet) (*net.IPNet, error) { - newSubnet := &net.IPNet{ - IP: subnet.IP, - Mask: subnet.Mask, - } - ones, bits := newSubnet.Mask.Size() - if ones == 0 { - return nil, fmt.Errorf("%s has only one subnet", subnet.String()) - } - zeroes := uint(bits - ones) - shift := zeroes % 8 - idx := ones/8 - 1 - if idx < 0 { - idx = 0 - } - if err := incByte(newSubnet, idx, shift); err != nil { - return nil, err - } - return newSubnet, nil -} - -// LastIPInSubnet gets the last IP in a subnet -func LastIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer - // re-parse to ensure clean network address - _, cidr, err := net.ParseCIDR(addr.String()) - if err != nil { - return nil, err - } - - ones, bits := cidr.Mask.Size() - if ones == bits { - return cidr.IP, nil - } - for i := range cidr.IP { - cidr.IP[i] = cidr.IP[i] | ^cidr.Mask[i] - } - return cidr.IP, nil -} - -// FirstIPInSubnet gets the first IP in a subnet -func FirstIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer - // re-parse to ensure clean network address - _, cidr, err := net.ParseCIDR(addr.String()) - if err != nil { - return nil, err - } - ones, bits := cidr.Mask.Size() - if ones == bits { - return cidr.IP, nil - } - cidr.IP[len(cidr.IP)-1]++ - return cidr.IP, nil -} diff --git a/libpod/network/subnet_test.go b/libpod/network/subnet_test.go deleted file mode 100644 index 55b2443bd..000000000 --- a/libpod/network/subnet_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package network - -import ( - "net" - "reflect" - "testing" -) - -func TestNextSubnet(t *testing.T) { - type args struct { - subnet *net.IPNet - } - tests := []struct { - name string - args args - want *net.IPNet - wantErr bool - }{ - {"class b", args{subnet: parseCIDR("192.168.0.0/16")}, parseCIDR("192.169.0.0/16"), false}, - {"class c", args{subnet: parseCIDR("192.168.1.0/24")}, parseCIDR("192.168.2.0/24"), false}, - } - for _, tt := range tests { - test := tt - t.Run(test.name, func(t *testing.T) { - got, err := NextSubnet(test.args.subnet) - if (err != nil) != test.wantErr { - t.Errorf("NextSubnet() error = %v, wantErr %v", err, test.wantErr) - return - } - if !reflect.DeepEqual(got, test.want) { - t.Errorf("NextSubnet() got = %v, want %v", got, test.want) - } - }) - } -} - -func TestFirstIPInSubnet(t *testing.T) { - tests := []struct { - name string - args *net.IPNet - want net.IP - wantErr bool - }{ - {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.0.1"), false}, - {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.1"), false}, - {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.0.1"), false}, - {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.1"), false}, - {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.129"), false}, - {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.0.0.1"), false}, - {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false}, - {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false}, - } - for _, tt := range tests { - test := tt - t.Run(test.name, func(t *testing.T) { - got, err := FirstIPInSubnet(test.args) - if (err != nil) != test.wantErr { - t.Errorf("FirstIPInSubnet() error = %v, wantErr %v", err, test.wantErr) - return - } - if !got.Equal(test.want) { - t.Errorf("FirstIPInSubnet() got = %v, want %v", got, test.want) - } - }) - } -} - -func TestLastIPInSubnet(t *testing.T) { - tests := []struct { - name string - args *net.IPNet - want net.IP - wantErr bool - }{ - {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.255.255"), false}, - {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.255"), false}, - {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.1.255"), false}, - {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.127"), false}, - {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.191"), false}, - {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.255.255.255"), false}, - {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false}, - {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false}, - } - for _, tt := range tests { - test := tt - t.Run(test.name, func(t *testing.T) { - got, err := LastIPInSubnet(test.args) - if (err != nil) != test.wantErr { - t.Errorf("LastIPInSubnet() error = %v, wantErr %v", err, test.wantErr) - return - } - if !got.Equal(test.want) { - t.Errorf("LastIPInSubnet() got = %v, want %v", got, test.want) - } - }) - } -} diff --git a/libpod/network/types/network.go b/libpod/network/types/network.go index c2c598f46..56bde716e 100644 --- a/libpod/network/types/network.go +++ b/libpod/network/types/network.go @@ -36,8 +36,7 @@ type Network struct { // InterfaceName is the network interface name on the host. NetworkInterface string `json:"network_interface,omitempty"` // Created contains the timestamp when this network was created. - // This is not guaranteed to stay exactly the same. - Created time.Time + Created time.Time `json:"created,omitempty"` // Subnets to use. Subnets []Subnet `json:"subnets,omitempty"` // IPv6Enabled if set to true an ipv6 subnet should be created for this net. @@ -92,9 +91,11 @@ func (n *IPNet) UnmarshalText(text []byte) error { } type Subnet struct { - // Subnet for this Network. + // Subnet for this Network in CIDR form. + // swagger:strfmt string Subnet IPNet `json:"subnet,omitempty"` // Gateway IP for this Network. + // swagger:strfmt string Gateway net.IP `json:"gateway,omitempty"` // LeaseRange contains the range where IP are leased. Optional. LeaseRange *LeaseRange `json:"lease_range,omitempty"` @@ -103,8 +104,10 @@ type Subnet struct { // LeaseRange contains the range where IP are leased. type LeaseRange struct { // StartIP first IP in the subnet which should be used to assign ips. + // swagger:strfmt string StartIP net.IP `json:"start_ip,omitempty"` // EndIP last IP in the subnet which should be used to assign ips. + // swagger:strfmt string EndIP net.IP `json:"end_ip,omitempty"` } diff --git a/libpod/network/util/filters.go b/libpod/network/util/filters.go index 48e769196..c3c80b352 100644 --- a/libpod/network/util/filters.go +++ b/libpod/network/util/filters.go @@ -28,12 +28,6 @@ func createFilterFuncs(key string, filterValues []string) (types.FilterFunc, err return util.StringMatchRegexSlice(net.Name, filterValues) }, nil - case "label": - // matches all labels - return func(net types.Network) bool { - return util.MatchLabelFilters(filterValues, net.Labels) - }, nil - case "driver": // matches network driver return func(net types.Network) bool { @@ -46,9 +40,39 @@ func createFilterFuncs(key string, filterValues []string) (types.FilterFunc, err return util.StringMatchRegexSlice(net.ID, filterValues) }, nil - // FIXME: What should we do with the old plugin filter - // TODO: add dangling, dns enabled, internal filter + // TODO: add dns enabled, internal filter + } + return createPruneFilterFuncs(key, filterValues) +} + +func GenerateNetworkPruneFilters(filters map[string][]string) ([]types.FilterFunc, error) { + filterFuncs := make([]types.FilterFunc, 0, len(filters)) + for key, filterValues := range filters { + filterFunc, err := createPruneFilterFuncs(key, filterValues) + if err != nil { + return nil, err + } + filterFuncs = append(filterFuncs, filterFunc) + } + return filterFuncs, nil +} + +func createPruneFilterFuncs(key string, filterValues []string) (types.FilterFunc, error) { + switch strings.ToLower(key) { + case "label": + // matches all labels + return func(net types.Network) bool { + return util.MatchLabelFilters(filterValues, net.Labels) + }, nil + case "until": + until, err := util.ComputeUntilTimestamp(filterValues) + if err != nil { + return nil, err + } + return func(net types.Network) bool { + return net.Created.Before(until) + }, nil default: return nil, errors.Errorf("invalid filter %q", key) } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index b0d4e0b2d..8ce4e1896 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -11,17 +11,15 @@ import ( "os/exec" "path/filepath" "regexp" - "sort" "strconv" "strings" "syscall" "time" - cnitypes "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/plugins/pkg/ns" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" - "github.com/containers/podman/v3/libpod/network" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/errorhandling" "github.com/containers/podman/v3/pkg/namespaces" "github.com/containers/podman/v3/pkg/netns" @@ -51,58 +49,57 @@ const ( persistentCNIDir = "/var/lib/cni" ) -// Get an OCICNI network config -func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.PortMapping, staticIP net.IP, staticMAC net.HardwareAddr, netDescriptions ContainerNetworkDescriptions) ocicni.PodNetwork { - var networkKey string - if len(networks) > 0 { - // This is inconsistent for >1 ctrNetwork, but it's probably the - // best we can do. - networkKey = networks[0] - } else { - networkKey = r.netPlugin.GetDefaultNetworkName() - } - ctrNetwork := ocicni.PodNetwork{ - Name: name, - Namespace: name, // TODO is there something else we should put here? We don't know about Kube namespaces - ID: id, - NetNS: nsPath, - RuntimeConfig: map[string]ocicni.RuntimeConfig{ - networkKey: {PortMappings: ports}, - }, +func (c *Container) getNetworkOptions() (types.NetworkOptions, error) { + opts := types.NetworkOptions{ + ContainerID: c.config.ID, + ContainerName: getCNIPodName(c), + } + // TODO remove ocicni PortMappings from container config and store as types PortMappings + if len(c.config.PortMappings) > 0 { + opts.PortMappings = ocicniPortsToNetTypesPorts(c.config.PortMappings) + } + networks, _, err := c.networks() + if err != nil { + return opts, err + } + aliases, err := c.runtime.state.GetAllNetworkAliases(c) + if err != nil { + return opts, err } - // If we have extra networks, add them - if len(networks) > 0 { - ctrNetwork.Networks = make([]ocicni.NetAttachment, len(networks)) - for i, netName := range networks { - ctrNetwork.Networks[i].Name = netName - if eth, exists := netDescriptions.getInterfaceByName(netName); exists { - ctrNetwork.Networks[i].Ifname = eth - } - } + // If the container requested special network options use this instead of the config. + // This is the case for container restore or network reload. + if c.perNetworkOpts != nil { + opts.Networks = c.perNetworkOpts + return opts, nil } - if staticIP != nil || staticMAC != nil { - // For static IP or MAC, we need to populate networks even if - // it's just the default. - if len(networks) == 0 { - // If len(networks) == 0 this is guaranteed to be the - // default ctrNetwork. - ctrNetwork.Networks = []ocicni.NetAttachment{{Name: networkKey}} - } - var rt ocicni.RuntimeConfig = ocicni.RuntimeConfig{PortMappings: ports} - if staticIP != nil { - rt.IP = staticIP.String() + // Update container map of interface descriptions + if err := c.setupNetworkDescriptions(networks); err != nil { + return opts, err + } + + nets := make(map[string]types.PerNetworkOptions, len(networks)) + for i, network := range networks { + eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(network) + if !exists { + return opts, errors.Errorf("no network interface name for container %s on network %s", c.config.ID, network) } - if staticMAC != nil { - rt.MAC = staticMAC.String() + netOpts := types.PerNetworkOptions{ + InterfaceName: eth, + Aliases: aliases[network], } - ctrNetwork.RuntimeConfig = map[string]ocicni.RuntimeConfig{ - networkKey: rt, + // only set the static ip/mac on the first network + if i == 0 { + if c.config.StaticIP != nil { + netOpts.StaticIPs = []net.IP{c.config.StaticIP} + } + netOpts.StaticMAC = c.config.StaticMAC } + nets[network] = netOpts } - - return ctrNetwork + opts.Networks = nets + return opts, nil } type RootlessCNI struct { @@ -571,9 +568,9 @@ func setPrimaryMachineIP() error { return os.Setenv("PODMAN_MACHINE_HOST", addr.IP.String()) } -// setUpOCICNIPod will set up the cni networks, on error it will also tear down the cni +// setUpNetwork will set up the the networks, on error it will also tear down the cni // networks. If rootless it will join/create the rootless cni namespace. -func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResult, error) { +func (r *Runtime) setUpNetwork(ns string, opts types.NetworkOptions) (map[string]types.StatusBlock, error) { if r.config.MachineEnabled() { if err := setPrimaryMachineIP(); err != nil { return nil, err @@ -583,16 +580,10 @@ func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResu if err != nil { return nil, err } - var results []ocicni.NetResult + var results map[string]types.StatusBlock setUpPod := func() error { - results, err = r.netPlugin.SetUpPod(podNetwork) - if err != nil { - if err2 := r.netPlugin.TearDownPod(podNetwork); err2 != nil { - logrus.Errorf("Error tearing down partially created network namespace for container %s: %v", podNetwork.ID, err2) - } - return errors.Wrapf(err, "error configuring network namespace for container %s", podNetwork.ID) - } - return nil + results, err = r.network.Setup(ns, types.SetupOptions{NetworkOptions: opts}) + return err } // rootlessCNINS is nil if we are root if rootlessCNINS != nil { @@ -609,7 +600,7 @@ func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResu // If we are in the pod network namespace use the pod name otherwise the container name func getCNIPodName(c *Container) string { if c.config.NetMode.IsPod() || c.IsInfra() { - pod, err := c.runtime.GetPod(c.PodID()) + pod, err := c.runtime.state.Pod(c.PodID()) if err == nil { return pod.Name() } @@ -618,26 +609,7 @@ func getCNIPodName(c *Container) string { } // Create and configure a new network namespace for a container -func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Result, error) { - var requestedIP net.IP - if ctr.requestedIP != nil { - requestedIP = ctr.requestedIP - // cancel request for a specific IP in case the container is reused later - ctr.requestedIP = nil - } else { - requestedIP = ctr.config.StaticIP - } - - var requestedMAC net.HardwareAddr - if ctr.requestedMAC != nil { - requestedMAC = ctr.requestedMAC - // cancel request for a specific MAC in case the container is reused later - ctr.requestedMAC = nil - } else { - requestedMAC = ctr.config.StaticMAC - } - - podName := getCNIPodName(ctr) +func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (map[string]types.StatusBlock, error) { networks, _, err := ctr.networks() if err != nil { return nil, err @@ -648,39 +620,15 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re return nil, nil } - // Update container map of interface descriptions - if err := ctr.setupNetworkDescriptions(networks); err != nil { - return nil, err - } - podNetwork := r.getPodNetwork(ctr.ID(), podName, ctrNS.Path(), networks, ctr.config.PortMappings, requestedIP, requestedMAC, ctr.state.NetInterfaceDescriptions) - aliases, err := ctr.runtime.state.GetAllNetworkAliases(ctr) + netOpts, err := ctr.getNetworkOptions() if err != nil { return nil, err } - if len(aliases) > 0 { - podNetwork.Aliases = aliases - } - - results, err := r.setUpOCICNIPod(podNetwork) - if err != nil { - return nil, err - } - - networkStatus := make([]*cnitypes.Result, 0) - for idx, r := range results { - logrus.Debugf("[%d] CNI result: %v", idx, r.Result) - resultCurrent, err := cnitypes.GetResult(r.Result) - if err != nil { - return nil, errors.Wrapf(err, "error parsing CNI plugin result %q: %v", r.Result, err) - } - networkStatus = append(networkStatus, resultCurrent) - } - - return networkStatus, nil + return r.setUpNetwork(ctrNS.Path(), netOpts) } // Create and configure a new network namespace for a container -func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result, retErr error) { +func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q map[string]types.StatusBlock, retErr error) { ctrNS, err := netns.NewNS() if err != nil { return nil, nil, errors.Wrapf(err, "error creating network namespace for container %s", ctr.ID()) @@ -698,7 +646,7 @@ func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result, logrus.Debugf("Made network namespace at %s for container %s", ctrNS.Path(), ctr.ID()) - networkStatus := []*cnitypes.Result{} + var networkStatus map[string]types.StatusBlock if !ctr.config.NetMode.IsSlirp4netns() { networkStatus, err = r.configureNetNS(ctr, ctrNS) } @@ -797,14 +745,14 @@ func (r *Runtime) closeNetNS(ctr *Container) error { // Tear down a container's CNI network configuration and joins the // rootless net ns as rootless user -func (r *Runtime) teardownOCICNIPod(podNetwork ocicni.PodNetwork) error { +func (r *Runtime) teardownNetwork(ns string, opts types.NetworkOptions) error { rootlessCNINS, err := r.GetRootlessCNINetNs(false) if err != nil { return err } tearDownPod := func() error { - err := r.netPlugin.TearDownPod(podNetwork) - return errors.Wrapf(err, "error tearing down CNI namespace configuration for container %s", podNetwork.ID) + err := r.network.Teardown(ns, types.TeardownOptions{NetworkOptions: opts}) + return errors.Wrapf(err, "error tearing down network namespace configuration for container %s", opts.ContainerID) } // rootlessCNINS is nil if we are root @@ -837,27 +785,11 @@ func (r *Runtime) teardownCNI(ctr *Container) error { } if !ctr.config.NetMode.IsSlirp4netns() && len(networks) > 0 { - var requestedIP net.IP - if ctr.requestedIP != nil { - requestedIP = ctr.requestedIP - // cancel request for a specific IP in case the container is reused later - ctr.requestedIP = nil - } else { - requestedIP = ctr.config.StaticIP - } - - var requestedMAC net.HardwareAddr - if ctr.requestedMAC != nil { - requestedMAC = ctr.requestedMAC - // cancel request for a specific MAC in case the container is reused later - ctr.requestedMAC = nil - } else { - requestedMAC = ctr.config.StaticMAC + netOpts, err := ctr.getNetworkOptions() + if err != nil { + return err } - - podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), networks, ctr.config.PortMappings, requestedIP, requestedMAC, ctr.state.NetInterfaceDescriptions) - err = r.teardownOCICNIPod(podNetwork) - return err + return r.teardownNetwork(ctr.state.NetNS.Path(), netOpts) } return nil } @@ -918,40 +850,15 @@ func isBridgeNetMode(n namespaces.NetworkMode) error { // single MAC or IP. // Only works on root containers at present, though in the future we could // extend this to stop + restart slirp4netns -func (r *Runtime) reloadContainerNetwork(ctr *Container) ([]*cnitypes.Result, error) { +func (r *Runtime) reloadContainerNetwork(ctr *Container) (map[string]types.StatusBlock, error) { if ctr.state.NetNS == nil { return nil, errors.Wrapf(define.ErrCtrStateInvalid, "container %s network is not configured, refusing to reload", ctr.ID()) } if err := isBridgeNetMode(ctr.config.NetMode); err != nil { return nil, err } - logrus.Infof("Going to reload container %s network", ctr.ID()) - var requestedIP net.IP - var requestedMAC net.HardwareAddr - // Set requested IP and MAC address, if possible. - if len(ctr.state.NetworkStatus) == 1 { - result := ctr.state.NetworkStatus[0] - if len(result.IPs) == 1 { - resIP := result.IPs[0] - - requestedIP = resIP.Address.IP - ctr.requestedIP = requestedIP - logrus.Debugf("Going to preserve container %s IP address %s", ctr.ID(), ctr.requestedIP.String()) - - if resIP.Interface != nil && *resIP.Interface < len(result.Interfaces) && *resIP.Interface >= 0 { - var err error - requestedMAC, err = net.ParseMAC(result.Interfaces[*resIP.Interface].Mac) - if err != nil { - return nil, errors.Wrapf(err, "error parsing container %s MAC address %s", ctr.ID(), result.Interfaces[*resIP.Interface].Mac) - } - ctr.requestedMAC = requestedMAC - logrus.Debugf("Going to preserve container %s MAC address %s", ctr.ID(), ctr.requestedMAC.String()) - } - } - } - err := r.teardownCNI(ctr) if err != nil { // teardownCNI will error if the iptables rules do not exists and this is the case after @@ -966,9 +873,40 @@ func (r *Runtime) reloadContainerNetwork(ctr *Container) ([]*cnitypes.Result, er } } - // teardownCNI will clean the requested IP and MAC so we need to set them again - ctr.requestedIP = requestedIP - ctr.requestedMAC = requestedMAC + aliases, err := ctr.runtime.state.GetAllNetworkAliases(ctr) + if err != nil { + return nil, err + } + + // Set the same network settings as before.. + netStatus := ctr.getNetworkStatus() + netOpts := make(map[string]types.PerNetworkOptions, len(netStatus)) + for network, status := range netStatus { + perNetOpts := types.PerNetworkOptions{} + for name, netInt := range status.Interfaces { + perNetOpts = types.PerNetworkOptions{ + InterfaceName: name, + Aliases: aliases[network], + StaticMAC: netInt.MacAddress, + } + for _, netAddress := range netInt.Networks { + perNetOpts.StaticIPs = append(perNetOpts.StaticIPs, netAddress.Subnet.IP) + } + // Normally interfaces have a length of 1, only for some special cni configs we could get more. + // For now just use the first interface to get the ips this should be good enough for most cases. + break + } + if perNetOpts.InterfaceName == "" { + eth, exists := ctr.state.NetInterfaceDescriptions.getInterfaceByName(network) + if !exists { + return nil, errors.Errorf("no network interface name for container %s on network %s", ctr.config.ID, network) + } + perNetOpts.InterfaceName = eth + } + netOpts[network] = perNetOpts + } + ctr.perNetworkOpts = netOpts + return r.configureNetNS(ctr, ctr.state.NetNS) } @@ -1047,27 +985,26 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e // Set network namespace path settings.SandboxKey = c.state.NetNS.Path() + netStatus := c.getNetworkStatus() // If this is empty, we're probably slirp4netns - if len(c.state.NetworkStatus) == 0 { + if len(netStatus) == 0 { return settings, nil } - // If we have CNI networks - handle that here + // If we have networks - handle that here if len(networks) > 0 { - if len(networks) != len(c.state.NetworkStatus) { - return nil, errors.Wrapf(define.ErrInternal, "network inspection mismatch: asked to join %d CNI network(s) %v, but have information on %d network(s)", len(networks), networks, len(c.state.NetworkStatus)) + if len(networks) != len(netStatus) { + return nil, errors.Wrapf(define.ErrInternal, "network inspection mismatch: asked to join %d network(s) %v, but have information on %d network(s)", len(networks), networks, len(netStatus)) } settings.Networks = make(map[string]*define.InspectAdditionalNetwork) - // CNI results should be in the same order as the list of - // networks we pass into CNI. - for index, name := range networks { - cniResult := c.state.NetworkStatus[index] + for _, name := range networks { + result := netStatus[name] addedNet := new(define.InspectAdditionalNetwork) addedNet.NetworkID = name - basicConfig, err := resultToBasicNetworkConfig(cniResult) + basicConfig, err := resultToBasicNetworkConfig(result) if err != nil { return nil, err } @@ -1089,19 +1026,19 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e } // If not joining networks, we should have at most 1 result - if len(c.state.NetworkStatus) > 1 { - return nil, errors.Wrapf(define.ErrInternal, "should have at most 1 CNI result if not joining networks, instead got %d", len(c.state.NetworkStatus)) + if len(netStatus) > 1 { + return nil, errors.Wrapf(define.ErrInternal, "should have at most 1 network status result if not joining networks, instead got %d", len(netStatus)) } - if len(c.state.NetworkStatus) == 1 { - basicConfig, err := resultToBasicNetworkConfig(c.state.NetworkStatus[0]) - if err != nil { - return nil, err + if len(netStatus) == 1 { + for _, status := range netStatus { + basicConfig, err := resultToBasicNetworkConfig(status) + if err != nil { + return nil, err + } + settings.InspectBasicNetworkConfig = basicConfig } - - settings.InspectBasicNetworkConfig = basicConfig } - return settings, nil } @@ -1130,45 +1067,40 @@ func (c *Container) setupNetworkDescriptions(networks []string) error { // resultToBasicNetworkConfig produces an InspectBasicNetworkConfig from a CNI // result -func resultToBasicNetworkConfig(result *cnitypes.Result) (define.InspectBasicNetworkConfig, error) { +func resultToBasicNetworkConfig(result types.StatusBlock) (define.InspectBasicNetworkConfig, error) { config := define.InspectBasicNetworkConfig{} - - for _, ctrIP := range result.IPs { - size, _ := ctrIP.Address.Mask.Size() - switch { - case ctrIP.Version == "4" && config.IPAddress == "": - config.IPAddress = ctrIP.Address.IP.String() - config.IPPrefixLen = size - config.Gateway = ctrIP.Gateway.String() - if ctrIP.Interface != nil && *ctrIP.Interface < len(result.Interfaces) && *ctrIP.Interface >= 0 { - config.MacAddress = result.Interfaces[*ctrIP.Interface].Mac - } - case ctrIP.Version == "4" && config.IPAddress != "": - config.SecondaryIPAddresses = append(config.SecondaryIPAddresses, ctrIP.Address.String()) - if ctrIP.Interface != nil && *ctrIP.Interface < len(result.Interfaces) && *ctrIP.Interface >= 0 { - config.AdditionalMacAddresses = append(config.AdditionalMacAddresses, result.Interfaces[*ctrIP.Interface].Mac) + for _, netInt := range result.Interfaces { + for _, netAddress := range netInt.Networks { + size, _ := netAddress.Subnet.Mask.Size() + if netAddress.Subnet.IP.To4() != nil { + //ipv4 + if config.IPAddress == "" { + config.IPAddress = netAddress.Subnet.IP.String() + config.IPPrefixLen = size + config.Gateway = netAddress.Gateway.String() + } else { + config.SecondaryIPAddresses = append(config.SecondaryIPAddresses, netAddress.Subnet.IP.String()) + } + } else { + //ipv6 + if config.GlobalIPv6Address == "" { + config.GlobalIPv6Address = netAddress.Subnet.IP.String() + config.GlobalIPv6PrefixLen = size + config.IPv6Gateway = netAddress.Gateway.String() + } else { + config.SecondaryIPv6Addresses = append(config.SecondaryIPv6Addresses, netAddress.Subnet.IP.String()) + } } - case ctrIP.Version == "6" && config.IPAddress == "": - config.GlobalIPv6Address = ctrIP.Address.IP.String() - config.GlobalIPv6PrefixLen = size - config.IPv6Gateway = ctrIP.Gateway.String() - case ctrIP.Version == "6" && config.IPAddress != "": - config.SecondaryIPv6Addresses = append(config.SecondaryIPv6Addresses, ctrIP.Address.String()) - default: - return config, errors.Wrapf(define.ErrInternal, "unrecognized IP version %q", ctrIP.Version) + } + if config.MacAddress == "" { + config.MacAddress = netInt.MacAddress.String() + } else { + config.AdditionalMacAddresses = append(config.AdditionalMacAddresses, netInt.MacAddress.String()) } } - return config, nil } -// This is a horrible hack, necessary because CNI does not properly clean up -// after itself on an unclean reboot. Return what we're pretty sure is the path -// to CNI's internal files (it's not really exposed to us). -func getCNINetworksDir() (string, error) { - return filepath.Join(persistentCNIDir, "networks"), nil -} - type logrusDebugWriter struct { prefix string } @@ -1185,28 +1117,31 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro return err } + c.lock.Lock() + defer c.lock.Unlock() + networks, err := c.networksByNameIndex() if err != nil { return err } // check if network exists and if the input is a ID we get the name - // ocicni only uses names so it is important that we only use the name - netName, err = network.NormalizeName(c.runtime.config, netName) + // 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 } - index, nameExists := networks[netName] + _, nameExists := networks[netName] if !nameExists && len(networks) > 0 { return errors.Errorf("container %s is not connected to network %s", nameOrID, netName) } - c.lock.Lock() - defer c.lock.Unlock() 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 @@ -1221,41 +1156,38 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro return errors.Wrapf(define.ErrNoNetwork, "unable to disconnect %s from %s", nameOrID, netName) } - podConfig := c.runtime.getPodNetwork(c.ID(), c.Name(), c.state.NetNS.Path(), []string{netName}, c.config.PortMappings, nil, nil, c.state.NetInterfaceDescriptions) - if err := c.runtime.teardownOCICNIPod(podConfig); err != nil { - return err + opts := types.NetworkOptions{ + ContainerID: c.config.ID, + ContainerName: getCNIPodName(c), + } + if len(c.config.PortMappings) > 0 { + opts.PortMappings = ocicniPortsToNetTypesPorts(c.config.PortMappings) + } + eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(netName) + if !exists { + return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, netName) + } + opts.Networks = map[string]types.PerNetworkOptions{ + netName: { + InterfaceName: eth, + }, } - // update network status if container is not running - networkStatus := c.state.NetworkStatus - // clip out the index of the network - tmpNetworkStatus := make([]*cnitypes.Result, 0, len(networkStatus)-1) - for k, v := range networkStatus { - if index != k { - tmpNetworkStatus = append(tmpNetworkStatus, v) - } + if err := c.runtime.teardownNetwork(c.state.NetNS.Path(), opts); err != nil { + return err } - c.state.NetworkStatus = tmpNetworkStatus + + // update network status if container is running + delete(networkStatus, netName) + c.state.NetworkStatus = networkStatus err = c.save() if err != nil { return err } - // OCICNI will set the loopback adapter down on teardown so we should set it up again - err = c.state.NetNS.Do(func(_ ns.NetNS) error { - link, err := netlink.LinkByName("lo") - if err != nil { - return err - } - err = netlink.LinkSetUp(link) - return err - }) - if err != nil { - logrus.Warnf("failed to set loopback adapter up in the container: %v", 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(tmpNetworkStatus) > 0 { + if rootless.IsRootless() && len(networkStatus) > 0 { return c.reloadRootlessRLKPortMapping() } return nil @@ -1268,24 +1200,28 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e return err } + c.lock.Lock() + defer c.lock.Unlock() + networks, err := c.networksByNameIndex() if err != nil { return err } // check if network exists and if the input is a ID we get the name - // ocicni only uses names so it is important that we only use the name - netName, err = network.NormalizeName(c.runtime.config, netName) + // 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 } - c.lock.Lock() - defer c.lock.Unlock() if err := c.syncContainer(); err != nil { return err } + // get network status before we connect + networkStatus := c.getNetworkStatus() + if err := c.runtime.state.NetworkConnect(c, netName, aliases); err != nil { return err } @@ -1305,10 +1241,26 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e if err := c.setupNetworkDescriptions(ctrNetworks); err != nil { return err } - podConfig := c.runtime.getPodNetwork(c.ID(), c.Name(), c.state.NetNS.Path(), []string{netName}, c.config.PortMappings, nil, nil, c.state.NetInterfaceDescriptions) - podConfig.Aliases = make(map[string][]string, 1) - podConfig.Aliases[netName] = aliases - results, err := c.runtime.setUpOCICNIPod(podConfig) + + opts := types.NetworkOptions{ + ContainerID: c.config.ID, + ContainerName: getCNIPodName(c), + } + if len(c.config.PortMappings) > 0 { + opts.PortMappings = ocicniPortsToNetTypesPorts(c.config.PortMappings) + } + eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(netName) + if !exists { + return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, netName) + } + opts.Networks = map[string]types.PerNetworkOptions{ + netName: { + Aliases: aliases, + InterfaceName: eth, + }, + } + + results, err := c.runtime.setUpNetwork(c.state.NetNS.Path(), opts) if err != nil { return err } @@ -1316,40 +1268,13 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e return errors.New("when adding aliases, results must be of length 1") } - networkResults := make([]*cnitypes.Result, 0) - for _, r := range results { - resultCurrent, err := cnitypes.GetResult(r.Result) - if err != nil { - return errors.Wrapf(err, "error parsing CNI plugin result %q: %v", r.Result, err) - } - networkResults = append(networkResults, resultCurrent) - } - // update network status - networkStatus := c.state.NetworkStatus - // if len is one and we confirmed earlier that the container is in - // fact connected to the network, then just return an empty slice - if len(networkStatus) == 0 { - c.state.NetworkStatus = append(c.state.NetworkStatus, networkResults...) - } else { - // build a list of network names so we can sort and - // get the new name's index - var networkNames []string - for name := range networks { - networkNames = append(networkNames, name) - } - networkNames = append(networkNames, netName) - // sort - sort.Strings(networkNames) - // get index of new network name - index := sort.SearchStrings(networkNames, netName) - // Append a zero value to to the slice - networkStatus = append(networkStatus, &cnitypes.Result{}) - // populate network status - copy(networkStatus[index+1:], networkStatus[index:]) - networkStatus[index] = networkResults[0] - c.state.NetworkStatus = networkStatus + 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 @@ -1379,3 +1304,27 @@ func (r *Runtime) ConnectContainerToNetwork(nameOrID, netName string, aliases [] } return ctr.NetworkConnect(nameOrID, netName, aliases) } + +// 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 ocicniPortsToNetTypesPorts(ports []ocicni.PortMapping) []types.PortMapping { + newPorts := make([]types.PortMapping, 0, len(ports)) + for _, port := range ports { + newPorts = append(newPorts, types.PortMapping{ + HostIP: port.HostIP, + HostPort: uint16(port.HostPort), + ContainerPort: uint16(port.ContainerPort), + Protocol: port.Protocol, + Range: 1, + }) + } + return newPorts +} diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go index a09027b72..a5c035757 100644 --- a/libpod/networking_slirp4netns.go +++ b/libpod/networking_slirp4netns.go @@ -615,14 +615,21 @@ func getRootlessPortChildIP(c *Container) string { return slirp4netnsIP.String() } - for _, r := range c.state.NetworkStatus { - for _, i := range r.IPs { - ipv4 := i.Address.IP.To4() - if ipv4 != nil { - return ipv4.String() + var ipv6 net.IP + for _, status := range c.getNetworkStatus() { + for _, netInt := range status.Interfaces { + for _, netAddress := range netInt.Networks { + ipv4 := netAddress.Subnet.IP.To4() + if ipv4 != nil { + return ipv4.String() + } + ipv6 = netAddress.Subnet.IP } } } + if ipv6 != nil { + return ipv6.String() + } return "" } diff --git a/libpod/runtime.go b/libpod/runtime.go index 761fa08a2..d2b3d36da 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -20,7 +20,6 @@ import ( "github.com/containers/buildah/pkg/parse" "github.com/containers/common/libimage" "github.com/containers/common/pkg/config" - "github.com/containers/common/pkg/defaultnet" "github.com/containers/common/pkg/secrets" "github.com/containers/image/v5/pkg/sysregistriesv2" is "github.com/containers/image/v5/storage" @@ -28,6 +27,8 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/libpod/lock" + "github.com/containers/podman/v3/libpod/network/cni" + nettypes "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/libpod/plugin" "github.com/containers/podman/v3/libpod/shutdown" "github.com/containers/podman/v3/pkg/cgroups" @@ -36,7 +37,6 @@ import ( "github.com/containers/podman/v3/pkg/util" "github.com/containers/storage" "github.com/containers/storage/pkg/unshare" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/docker/pkg/namesgenerator" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -80,7 +80,7 @@ type Runtime struct { defaultOCIRuntime OCIRuntime ociRuntimes map[string]OCIRuntime runtimeFlags []string - netPlugin ocicni.CNIPlugin + network nettypes.ContainerNetwork conmonPath string libimageRuntime *libimage.Runtime libimageEventsShutdown chan bool @@ -482,17 +482,20 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) { } } - // If we need to make a default network - do so now. - if err := defaultnet.Create(runtime.config.Network.DefaultNetwork, runtime.config.Network.DefaultSubnet, runtime.config.Network.NetworkConfigDir, runtime.config.Engine.StaticDir, runtime.config.Engine.MachineEnabled); err != nil { - logrus.Errorf("Failed to created default CNI network: %v", err) - } - - // Set up the CNI net plugin - netPlugin, err := ocicni.InitCNINoInotify(runtime.config.Network.DefaultNetwork, runtime.config.Network.NetworkConfigDir, "", runtime.config.Network.CNIPluginDirs...) + netInterface, err := cni.NewCNINetworkInterface(cni.InitConfig{ + CNIConfigDir: runtime.config.Network.NetworkConfigDir, + CNIPluginDirs: runtime.config.Network.CNIPluginDirs, + DefaultNetwork: runtime.config.Network.DefaultNetwork, + DefaultSubnet: runtime.config.Network.DefaultSubnet, + IsMachine: runtime.config.Engine.MachineEnabled, + // TODO use cni.lock + LockFile: filepath.Join(runtime.config.Network.NetworkConfigDir, "cni1.lock"), + }) if err != nil { - return errors.Wrapf(err, "error configuring CNI network plugin") + return errors.Wrapf(err, "could not create network interface") } - runtime.netPlugin = netPlugin + + runtime.network = netInterface // We now need to see if the system has restarted // We check for the presence of a file in our tmp directory to verify this @@ -1166,3 +1169,13 @@ func (r *Runtime) graphRootMountedFlag(mounts []spec.Mount) string { } return "" } + +// Network returns the network interface which is used by the runtime +func (r *Runtime) Network() nettypes.ContainerNetwork { + return r.network +} + +// Network returns the network interface which is used by the runtime +func (r *Runtime) GetDefaultNetworkName() string { + return r.config.Network.DefaultNetwork +} diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 7d3891f6e..d4f67a115 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -12,7 +12,6 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" - "github.com/containers/podman/v3/libpod/network" "github.com/containers/podman/v3/libpod/shutdown" "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/domain/entities/reports" @@ -248,7 +247,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai if len(ctr.config.Networks) > 0 { netNames := make([]string, 0, len(ctr.config.Networks)) for _, nameOrID := range ctr.config.Networks { - netName, err := network.NormalizeName(r.config, nameOrID) + netName, err := r.normalizeNetworkName(nameOrID) if err != nil { return nil, err } @@ -262,7 +261,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai if len(ctr.config.NetworkAliases) > 0 { netAliases := make(map[string][]string, len(ctr.config.NetworkAliases)) for nameOrID, aliases := range ctr.config.NetworkAliases { - netName, err := network.NormalizeName(r.config, nameOrID) + netName, err := r.normalizeNetworkName(nameOrID) if err != nil { return nil, err } |