diff --git a/common/Makefile b/common/Makefile index 9d7a1c3f5b..5f81135813 100644 --- a/common/Makefile +++ b/common/Makefile @@ -40,19 +40,15 @@ build-cross: $(call go-build,freebsd,386,${BUILDTAGS}) .PHONY: all -all: build-amd64 build-386 build-amd64-cni +all: build-amd64 build-386 .PHONY: build -build: build-amd64 build-386 build-amd64-cni +build: build-amd64 build-386 .PHONY: build-amd64 build-amd64: GOARCH=amd64 $(GO_BUILD) -tags $(BUILDTAGS) ./... -.PHONY: build-amd64-cni -build-amd64-cni: - GOARCH=amd64 $(GO_BUILD) -tags $(BUILDTAGS),cni ./... - .PHONY: build-386 build-386: ifneq ($(shell uname -s), Darwin) @@ -102,7 +98,6 @@ test: test-unit test-unit: netavark-testplugin go test --tags seccomp,$(BUILDTAGS) -v ./... go test --tags remote,$(BUILDTAGS) -v ./pkg/config - go test --tags cni,$(BUILDTAGS) -v ./libnetwork/cni clean: ## Clean artifacts $(MAKE) -C docs clean diff --git a/common/docs/containers.conf.5.md b/common/docs/containers.conf.5.md index a7f233709c..0e17c3a4ed 100644 --- a/common/docs/containers.conf.5.md +++ b/common/docs/containers.conf.5.md @@ -298,9 +298,9 @@ Logging driver for the container. Currently available options are k8s-file, jour **log_path**="" -Default path for container logs to be stored in. When empty, logs will be stored +Default path for container logs to be stored in. When empty, logs will be stored in the container's default storage and removed when the container is removed. -A subdirectory named with the container ID will be created under the specified +A subdirectory named with the container ID will be created under the specified path, and the log file will have the default name `ctr.log` within that directory. This option can be overridden by the `--log-opt` flag. @@ -418,34 +418,18 @@ Specified as "directory-on-host:directory-in-container:options". Example: "/db:/var/lib/db:ro". ## NETWORK TABLE -The `network` table contains settings pertaining to the management of CNI +The `network` table contains settings pertaining to the management of netavark plugins. **network_backend**="" Network backend determines what network driver will be used to set up and tear down container networks. -Valid values are "cni" and "netavark". -The default value is empty which means that it will automatically choose CNI or netavark. If there are -already containers/images or CNI networks preset it will choose CNI. +Valid value is "netavark". +The default value is empty which means that it will automatically choose netavark. Before changing this value all containers must be stopped otherwise it is likely that iptables rules and network interfaces might leak on the host. A reboot will fix this. -**cni_plugin_dirs**=[] - -List of paths to directories where CNI plugin binaries are located. - -The default list is: -``` -cni_plugin_dirs = [ - "/usr/local/libexec/cni", - "/usr/libexec/cni", - "/usr/local/lib/cni", - "/usr/lib/cni", - "/opt/cni/bin", -] -``` - **netavark_plugin_dirs**=[] List of directories that will be searched for netavark plugins. @@ -495,14 +479,12 @@ default_subnet_pools = [ **default_rootless_network_cmd**="pasta" -Configure which rootless network program to use by default. Valid options are -`slirp4netns` and `pasta` (default). +Configure which rootless network program to use by default. Only current valid option is +`pasta` (default). -**network_config_dir**="/etc/cni/net.d/" +**network_config_dir**="/etc/containers/networks/" Path to the directory where network configuration files are located. -For the CNI backend the default is __/etc/cni/net.d__ as root -and __$HOME/.config/cni/net.d__ as rootless. For the netavark backend "/etc/containers/networks" is used as root and "$graphroot/networks" as rootless. @@ -675,7 +657,6 @@ The following binaries are searched in these directories: - catatonit - netavark - pasta - - slirp4netns Podman machine uses it for these binaries: - gvproxy @@ -776,28 +757,6 @@ create new containers and pods in that namespace. The default namespace is "", which corresponds to no namespace. When no namespace is set, all containers and pods are visible. -**network_cmd_path**="" - -Path to the slirp4netns binary. - -**network_cmd_options**=[] - -Default options to pass to the slirp4netns binary. - -Valid options values are: - - - **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false. - - **mtu=MTU**: Specify the MTU to use for this network. (Default is `65520`). - - **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`). - - **enable_ipv6=true|false**: Enable IPv6. Default is true. (Required for `outbound_addr6`). - - **outbound_addr=INTERFACE**: Specify the outbound interface slirp should bind to (ipv4 traffic only). - - **outbound_addr=IPv4**: Specify the outbound ipv4 address slirp should bind to. - - **outbound_addr6=INTERFACE**: Specify the outbound interface slirp should bind to (ipv6 traffic only). - - **outbound_addr6=IPv6**: Specify the outbound ipv6 address slirp should bind to. - - **port_handler=rootlesskit**: Use rootlesskit for port forwarding. Default. - Note: Rootlesskit changes the source IP address of incoming packets to a IP address in the container network namespace, usually `10.0.2.100`. If your application requires the real source IP address, e.g. web server logs, use the slirp4netns port handler. The rootlesskit port handler is also used for rootless containers when connected to user-defined networks. - - **port_handler=slirp4netns**: Use the slirp4netns port forwarding, it is slower than rootlesskit but preserves the correct source IP address. This port handler cannot be used for user-defined networks. - **no_pivot_root**=false Whether to use chroot instead of pivot_root in the runtime. diff --git a/common/go.mod b/common/go.mod index 2c7facc35d..42635d4582 100644 --- a/common/go.mod +++ b/common/go.mod @@ -9,7 +9,6 @@ require ( github.com/checkpoint-restore/checkpointctl v1.4.0 github.com/checkpoint-restore/go-criu/v7 v7.2.0 github.com/containerd/platforms v0.2.1 - github.com/containernetworking/cni v1.3.0 github.com/containernetworking/plugins v1.8.0 github.com/containers/ocicrypt v1.2.1 github.com/coreos/go-systemd/v22 v22.6.0 diff --git a/common/libnetwork/cni/README.md b/common/libnetwork/cni/README.md deleted file mode 100644 index 6f57feff51..0000000000 --- a/common/libnetwork/cni/README.md +++ /dev/null @@ -1,10 +0,0 @@ -This package abstracts CNI from libpod. -It implements the `ContainerNetwork` interface defined in [libpod/network/types/network.go](../types/network.go) for the CNI backend. - - -## Testing -Run the tests with: -``` -go test -v -mod=vendor -cover ./libpod/network/cni/ -``` -Run the tests as root to also test setup/teardown. This will execute CNI and therefore the cni plugins have to be installed. diff --git a/common/libnetwork/cni/cni_conversion.go b/common/libnetwork/cni/cni_conversion.go deleted file mode 100644 index c89a867453..0000000000 --- a/common/libnetwork/cni/cni_conversion.go +++ /dev/null @@ -1,461 +0,0 @@ -//go:build (linux || freebsd) && cni - -package cni - -import ( - "encoding/json" - "errors" - "fmt" - "net" - "os" - "path/filepath" - "slices" - "strconv" - "strings" - "time" - - "github.com/containernetworking/cni/libcni" - "github.com/sirupsen/logrus" - internalutil "go.podman.io/common/libnetwork/internal/util" - "go.podman.io/common/libnetwork/types" - "go.podman.io/common/libnetwork/util" - "golang.org/x/sys/unix" -) - -func createNetworkFromCNIConfigList(conf *libcni.NetworkConfigList, confPath string) (*types.Network, error) { - network := types.Network{ - Name: conf.Name, - ID: getNetworkIDFromName(conf.Name), - Labels: map[string]string{}, - Options: map[string]string{}, - IPAMOptions: map[string]string{}, - } - - cniJSON := make(map[string]any) - err := json.Unmarshal(conf.Bytes, &cniJSON) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal network config %s: %w", conf.Name, err) - } - if args, ok := cniJSON["args"]; ok { - if key, ok := args.(map[string]any); ok { - // read network labels and options from the conf file - network.Labels = getNetworkArgsFromConfList(key, podmanLabelKey) - network.Options = getNetworkArgsFromConfList(key, podmanOptionsKey) - } - } - - t, err := fileTime(confPath) - if err != nil { - return nil, err - } - network.Created = t - - firstPlugin := conf.Plugins[0] - network.Driver = firstPlugin.Network.Type - - switch firstPlugin.Network.Type { - case types.BridgeNetworkDriver: - var bridge hostLocalBridge - err := json.Unmarshal(firstPlugin.Bytes, &bridge) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal the bridge plugin config in %s: %w", confPath, err) - } - network.NetworkInterface = bridge.BrName - - // if isGateway is false we have an internal network - if !bridge.IsGW { - network.Internal = true - } - - // set network options - if bridge.MTU != 0 { - network.Options[types.MTUOption] = strconv.Itoa(bridge.MTU) - } - if bridge.Vlan != 0 { - network.Options[types.VLANOption] = strconv.Itoa(bridge.Vlan) - } - - err = convertIPAMConfToNetwork(&network, &bridge.IPAM, confPath) - if err != nil { - return nil, err - } - - case types.MacVLANNetworkDriver, types.IPVLANNetworkDriver: - var vlan VLANConfig - err := json.Unmarshal(firstPlugin.Bytes, &vlan) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal the macvlan plugin config in %s: %w", confPath, err) - } - network.NetworkInterface = vlan.Master - - // set network options - if vlan.MTU != 0 { - network.Options[types.MTUOption] = strconv.Itoa(vlan.MTU) - } - - if vlan.Mode != "" { - network.Options[types.ModeOption] = vlan.Mode - } - - err = convertIPAMConfToNetwork(&network, &vlan.IPAM, confPath) - if err != nil { - return nil, err - } - - default: - // A warning would be good but users would get this warning every time so keep this at info level. - logrus.Infof("Unsupported CNI config type %s in %s, this network can still be used but inspect or list cannot show all information", - firstPlugin.Network.Type, confPath) - } - - // check if the dnsname plugin is configured - network.DNSEnabled = findPluginByName(conf.Plugins, "dnsname") != nil - - // now get isolation mode from firewall plugin - firewall := findPluginByName(conf.Plugins, "firewall") - if firewall != nil { - var firewallConf firewallConfig - err := json.Unmarshal(firewall.Bytes, &firewallConf) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal the firewall plugin config in %s: %w", confPath, err) - } - if firewallConf.IngressPolicy == ingressPolicySameBridge { - network.Options[types.IsolateOption] = "true" - } - } - - return &network, nil -} - -func findPluginByName(plugins []*libcni.NetworkConfig, name string) *libcni.NetworkConfig { - for i := range plugins { - if plugins[i].Network.Type == name { - return plugins[i] - } - } - return nil -} - -// convertIPAMConfToNetwork converts A cni IPAMConfig to libpod network subnets. -// It returns an array of subnets and an extra bool if dhcp is configured. -func convertIPAMConfToNetwork(network *types.Network, ipam *ipamConfig, confPath string) error { - switch ipam.PluginType { - case "": - network.IPAMOptions[types.Driver] = types.NoneIPAMDriver - case types.DHCPIPAMDriver: - network.IPAMOptions[types.Driver] = types.DHCPIPAMDriver - case types.HostLocalIPAMDriver: - network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver - for _, r := range ipam.Ranges { - for _, ipam := range r { - s := types.Subnet{} - - // Do not use types.ParseCIDR() because we want the ip to be - // the network address and not a random ip in the sub. - _, sub, err := net.ParseCIDR(ipam.Subnet) - if err != nil { - return err - } - s.Subnet = types.IPNet{IPNet: *sub} - - // gateway - var gateway net.IP - if ipam.Gateway != "" { - gateway = net.ParseIP(ipam.Gateway) - if gateway == nil { - return fmt.Errorf("failed to parse gateway ip %s", ipam.Gateway) - } - // convert to 4 byte if ipv4 - util.NormalizeIP(&gateway) - } else if !network.Internal { - // only add a gateway address if the network is not internal - gateway, err = util.FirstIPInSubnet(sub) - if err != nil { - return fmt.Errorf("failed to get first ip in subnet %s", sub.String()) - } - } - s.Gateway = gateway - - var rangeStart net.IP - var rangeEnd net.IP - if ipam.RangeStart != "" { - rangeStart = net.ParseIP(ipam.RangeStart) - if rangeStart == nil { - return fmt.Errorf("failed to parse range start ip %s", ipam.RangeStart) - } - } - if ipam.RangeEnd != "" { - rangeEnd = net.ParseIP(ipam.RangeEnd) - if rangeEnd == nil { - return fmt.Errorf("failed to parse range end ip %s", ipam.RangeEnd) - } - } - if rangeStart != nil || rangeEnd != nil { - s.LeaseRange = &types.LeaseRange{} - s.LeaseRange.StartIP = rangeStart - s.LeaseRange.EndIP = rangeEnd - } - if util.IsIPv6(s.Subnet.IP) { - network.IPv6Enabled = true - } - network.Subnets = append(network.Subnets, s) - } - } - default: - // This is not an error. While we only support certain ipam drivers, we - // cannot make it fail for unsupported ones. CNI is still able to use them, - // just our translation logic cannot convert this into a Network. - // For the same reason this is not warning, it would just be annoying for - // everyone using a unknown ipam driver. - logrus.Infof("unsupported ipam plugin %q in %s", ipam.PluginType, confPath) - network.IPAMOptions[types.Driver] = ipam.PluginType - } - return nil -} - -// getNetworkArgsFromConfList returns the map of args in a conflist, argType should be labels or options. -func getNetworkArgsFromConfList(args map[string]any, argType string) map[string]string { - if args, ok := args[argType]; ok { - if labels, ok := args.(map[string]any); ok { - result := make(map[string]string, len(labels)) - for k, v := range labels { - if v, ok := v.(string); ok { - result[k] = v - } - } - return result - } - } - return map[string]string{} -} - -// createCNIConfigListFromNetwork will create a cni config file from the given network. -// It returns the cni config and the path to the file where the config was written. -// Set writeToDisk to false to only add this network into memory. -func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writeToDisk bool) (*libcni.NetworkConfigList, string, error) { - var ( - routes []ipamRoute - ipamRanges [][]ipamLocalHostRangeConf - ipamConf *ipamConfig - err error - ) - - ipamDriver := network.IPAMOptions[types.Driver] - switch ipamDriver { - case types.HostLocalIPAMDriver: - defIpv4Route := false - defIpv6Route := false - for _, subnet := range network.Subnets { - ipam := newIPAMLocalHostRange(subnet.Subnet, subnet.LeaseRange, subnet.Gateway) - ipamRanges = append(ipamRanges, []ipamLocalHostRangeConf{*ipam}) - - // only add default route for not internal networks - if !network.Internal { - ipv6 := util.IsIPv6(subnet.Subnet.IP) - if !ipv6 && defIpv4Route { - continue - } - if ipv6 && defIpv6Route { - continue - } - - if ipv6 { - defIpv6Route = true - } else { - defIpv4Route = true - } - route, err := newIPAMDefaultRoute(ipv6) - if err != nil { - return nil, "", err - } - routes = append(routes, route) - } - } - conf := newIPAMHostLocalConf(routes, ipamRanges) - ipamConf = &conf - case types.DHCPIPAMDriver: - ipamConf = &ipamConfig{PluginType: "dhcp"} - - case types.NoneIPAMDriver: - // do nothing - default: - return nil, "", fmt.Errorf("unsupported ipam driver %q", ipamDriver) - } - - opts, err := parseOptions(network.Options, network.Driver) - if err != nil { - return nil, "", err - } - - isGateway := true - ipMasq := true - if network.Internal { - isGateway = false - ipMasq = false - } - // create CNI plugin configuration - // explicitly use CNI version 0.4.0 here, to use v1.0.0 at least containernetwork-plugins-1.0.1 has to be installed - // the dnsname plugin also needs to be updated for 1.0.0 - // TODO change to 1.0.0 when most distros support it - ncList := newNcList(network.Name, "0.4.0", network.Labels, network.Options) - var plugins []any - - switch network.Driver { - case types.BridgeNetworkDriver: - bridge := newHostLocalBridge(network.NetworkInterface, isGateway, ipMasq, opts.mtu, opts.vlan, ipamConf) - plugins = append(plugins, bridge, newPortMapPlugin(), newFirewallPlugin(opts.isolate), newTuningPlugin()) - // if we find the dnsname plugin we add configuration for it - if hasDNSNamePlugin(n.cniPluginDirs) && network.DNSEnabled { - // Note: in the future we might like to allow for dynamic domain names - plugins = append(plugins, newDNSNamePlugin(defaultPodmanDomainName)) - } - - case types.MacVLANNetworkDriver: - plugins = append(plugins, newVLANPlugin(types.MacVLANNetworkDriver, network.NetworkInterface, opts.vlanPluginMode, opts.mtu, ipamConf)) - - case types.IPVLANNetworkDriver: - plugins = append(plugins, newVLANPlugin(types.IPVLANNetworkDriver, network.NetworkInterface, opts.vlanPluginMode, opts.mtu, ipamConf)) - - default: - return nil, "", fmt.Errorf("driver %q is not supported by cni", network.Driver) - } - ncList["plugins"] = plugins - b, err := json.MarshalIndent(ncList, "", " ") - if err != nil { - return nil, "", err - } - cniPathName := "" - if writeToDisk { - if err := os.MkdirAll(n.cniConfigDir, 0o755); err != nil { - return nil, "", err - } - cniPathName = filepath.Join(n.cniConfigDir, network.Name+".conflist") - err = os.WriteFile(cniPathName, b, 0o644) - if err != nil { - return nil, "", err - } - t, err := fileTime(cniPathName) - if err != nil { - return nil, "", err - } - network.Created = t - } else { - network.Created = time.Now() - } - config, err := libcni.ConfListFromBytes(b) - if err != nil { - return nil, "", err - } - return config, cniPathName, nil -} - -func convertSpecgenPortsToCNIPorts(ports []types.PortMapping) ([]cniPortMapEntry, error) { - cniPorts := make([]cniPortMapEntry, 0, len(ports)) - for _, port := range ports { - if port.Protocol == "" { - return nil, errors.New("port protocol should not be empty") - } - for protocol := range strings.SplitSeq(port.Protocol, ",") { - if !slices.Contains([]string{"tcp", "udp", "sctp"}, protocol) { - return nil, fmt.Errorf("unknown port protocol %s", protocol) - } - cniPort := cniPortMapEntry{ - HostPort: int(port.HostPort), - ContainerPort: int(port.ContainerPort), - HostIP: port.HostIP, - Protocol: protocol, - } - cniPorts = append(cniPorts, cniPort) - for i := 1; i < int(port.Range); i++ { - cniPort := cniPortMapEntry{ - HostPort: int(port.HostPort) + i, - ContainerPort: int(port.ContainerPort) + i, - HostIP: port.HostIP, - Protocol: protocol, - } - cniPorts = append(cniPorts, cniPort) - } - } - } - return cniPorts, nil -} - -func removeMachinePlugin(conf *libcni.NetworkConfigList) *libcni.NetworkConfigList { - plugins := make([]*libcni.NetworkConfig, 0, len(conf.Plugins)) - for _, net := range conf.Plugins { - if net.Network.Type != "podman-machine" { - plugins = append(plugins, net) - } - } - conf.Plugins = plugins - return conf -} - -type options struct { - vlan int - mtu int - vlanPluginMode string - isolate bool -} - -func parseOptions(networkOptions map[string]string, networkDriver string) (*options, error) { - opt := &options{} - var err error - for k, v := range networkOptions { - switch k { - case types.MTUOption: - opt.mtu, err = internalutil.ParseMTU(v) - if err != nil { - return nil, err - } - - case types.VLANOption: - opt.vlan, err = internalutil.ParseVlan(v) - if err != nil { - return nil, err - } - - case types.ModeOption: - switch networkDriver { - case types.MacVLANNetworkDriver: - if !slices.Contains(types.ValidMacVLANModes, v) { - return nil, fmt.Errorf("unknown macvlan mode %q", v) - } - case types.IPVLANNetworkDriver: - if !slices.Contains(types.ValidIPVLANModes, v) { - return nil, fmt.Errorf("unknown ipvlan mode %q", v) - } - default: - return nil, fmt.Errorf("cannot set option \"mode\" with driver %q", networkDriver) - } - opt.vlanPluginMode = v - - case types.IsolateOption: - if networkDriver != types.BridgeNetworkDriver { - return nil, errors.New("isolate option is only supported with the bridge driver") - } - opt.isolate, err = strconv.ParseBool(v) - if err != nil { - return nil, fmt.Errorf("failed to parse isolate option: %w", err) - } - - default: - return nil, fmt.Errorf("unsupported network option %s", k) - } - } - return opt, nil -} - -func fileTime(file string) (time.Time, error) { - var st unix.Stat_t - for { - err := unix.Stat(file, &st) - if err == nil { - break - } - if err != unix.EINTR { //nolint:errorlint // unix errors are bare - return time.Time{}, &os.PathError{Path: file, Op: "stat", Err: err} - } - } - return time.Unix(int64(st.Ctim.Sec), int64(st.Ctim.Nsec)), nil //nolint:unconvert // On some platforms Sec and Nsec are int32. -} diff --git a/common/libnetwork/cni/cni_exec.go b/common/libnetwork/cni/cni_exec.go deleted file mode 100644 index e4c47f5d73..0000000000 --- a/common/libnetwork/cni/cni_exec.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2016 CNI authors -// Copyright 2021 Podman authors -// -// This code has been originally copied from github.com/containernetworking/cni -// but has been changed to better fit the Podman use case. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build (linux || freebsd) && cni - -package cni - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/containernetworking/cni/pkg/invoke" - "github.com/containernetworking/cni/pkg/version" - "go.podman.io/storage/pkg/unshare" -) - -type cniExec struct { - version.PluginDecoder -} - -type cniPluginError struct { - plugin string - Code uint `json:"code"` - Msg string `json:"msg"` - Details string `json:"details,omitempty"` -} - -// Error returns a nicely formatted error message for the cni plugin errors. -func (e *cniPluginError) Error() string { - err := fmt.Sprintf("cni plugin %s failed", e.plugin) - if e.Msg != "" { - err = fmt.Sprintf("%s: %s", err, e.Msg) - } else if e.Code > 0 { - err = fmt.Sprintf("%s with error code %d", err, e.Code) - } - if e.Details != "" { - err = fmt.Sprintf("%s: %s", err, e.Details) - } - return err -} - -// ExecPlugin execute the cni plugin. Returns the stdout of the plugin or an error. -func (e *cniExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) { - stdout := &bytes.Buffer{} - stderr := &bytes.Buffer{} - c := exec.CommandContext(ctx, pluginPath) - c.Env = environ - c.Stdin = bytes.NewBuffer(stdinData) - c.Stdout = stdout - c.Stderr = stderr - - // The dnsname plugin tries to use XDG_RUNTIME_DIR to store files. - // podman run will have XDG_RUNTIME_DIR set and thus the cni plugin can use - // it. The problem is that XDG_RUNTIME_DIR is unset for the conmon process - // for rootful users. This causes issues since the cleanup process is spawned - // by conmon and thus not have XDG_RUNTIME_DIR set to same value as podman run. - // Because of it dnsname will not find the config files and cannot correctly cleanup. - // To fix this we should also unset XDG_RUNTIME_DIR for the cni plugins as rootful. - if !unshare.IsRootless() { - c.Env = append(c.Env, "XDG_RUNTIME_DIR=") - } - - // The CNI plugins need access to iptables in $PATH. As it turns out debian doesn't put - // /usr/sbin in $PATH for rootless users. This will break rootless networking completely. - // We might break existing users and we cannot expect everyone to change their $PATH so - // let's add /usr/sbin to $PATH ourselves. - path := os.Getenv("PATH") - if !strings.Contains(path, "/usr/sbin") { - path += ":/usr/sbin" - c.Env = append(c.Env, "PATH="+path) - } - - err := c.Run() - if err != nil { - return nil, annotatePluginError(err, pluginPath, stdout.Bytes(), stderr.Bytes()) - } - return stdout.Bytes(), nil -} - -// annotatePluginError parses the common cni plugin error json. -func annotatePluginError(err error, plugin string, stdout, stderr []byte) error { - pluginName := filepath.Base(plugin) - emsg := cniPluginError{ - plugin: pluginName, - } - if len(stdout) == 0 { - if len(stderr) == 0 { - emsg.Msg = err.Error() - } else { - emsg.Msg = string(stderr) - } - } else if perr := json.Unmarshal(stdout, &emsg); perr != nil { - emsg.Msg = fmt.Sprintf("failed to unmarshal error message %q: %v", string(stdout), perr) - } - return &emsg -} - -// FindInPath finds the plugin in the given paths. -func (e *cniExec) FindInPath(plugin string, paths []string) (string, error) { - return invoke.FindInPath(plugin, paths) -} diff --git a/common/libnetwork/cni/cni_suite_test.go b/common/libnetwork/cni/cni_suite_test.go deleted file mode 100644 index d7bca07b9b..0000000000 --- a/common/libnetwork/cni/cni_suite_test.go +++ /dev/null @@ -1,49 +0,0 @@ -//go:build (linux || freebsd) && cni - -package cni_test - -import ( - "os" - "path/filepath" - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "go.podman.io/common/internal/attributedstring" - "go.podman.io/common/libnetwork/cni" - "go.podman.io/common/libnetwork/types" - "go.podman.io/common/pkg/config" -) - -var cniPluginDirs = []string{ - "/usr/libexec/cni", - "/usr/lib/cni", - "/usr/local/lib/cni", - "/opt/cni/bin", -} - -func TestCni(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "CNI Suite") -} - -func getNetworkInterface(cniConfDir string) (types.ContainerNetwork, error) { - return cni.NewCNINetworkInterface(&cni.InitConfig{ - CNIConfigDir: cniConfDir, - Config: &config.Config{ - Network: config.NetworkConfig{ - CNIPluginDirs: attributedstring.NewSlice(cniPluginDirs), - }, - }, - }) -} - -func SkipIfNoDnsname() { - for _, path := range cniPluginDirs { - f, err := os.Stat(filepath.Join(path, "dnsname")) - if err == nil && f.Mode().IsRegular() { - return - } - } - Skip("dnsname cni plugin needs to be installed for this test") -} diff --git a/common/libnetwork/cni/cni_types.go b/common/libnetwork/cni/cni_types.go deleted file mode 100644 index e85a11b493..0000000000 --- a/common/libnetwork/cni/cni_types.go +++ /dev/null @@ -1,286 +0,0 @@ -//go:build (linux || freebsd) && cni - -package cni - -import ( - "net" - "path/filepath" - - "go.podman.io/common/libnetwork/types" - "go.podman.io/storage/pkg/fileutils" -) - -const ( - defaultIPv4Route = "0.0.0.0/0" - defaultIPv6Route = "::/0" - // defaultPodmanDomainName is used for the dnsname plugin to define - // a localized domain name for a created network. - defaultPodmanDomainName = "dns.podman" - - // cniDeviceName is the default name for a new bridge, it should be suffixed with an integer. - cniDeviceName = "cni-podman" - - // podmanLabelKey key used to store the podman network label in a cni config. - podmanLabelKey = "podman_labels" - - // podmanOptionsKey key used to store the podman network options in a cni config. - podmanOptionsKey = "podman_options" - - // ingressPolicySameBridge is used to only allow connection on the same bridge network. - ingressPolicySameBridge = "same-bridge" -) - -// cniPortMapEntry struct is used by the portmap plugin -// https://github.com/containernetworking/plugins/blob/649e0181fe7b3a61e708f3e4249a798f57f25cc5/plugins/meta/portmap/main.go#L43-L50 -type cniPortMapEntry struct { - HostPort int `json:"hostPort"` - ContainerPort int `json:"containerPort"` - Protocol string `json:"protocol"` - HostIP string `json:"hostIP,omitempty"` -} - -// 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"` - Capabilities map[string]bool `json:"capabilities,omitempty"` -} - -// 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"` -} - -// 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"` -} - -// VLANConfig describes the macvlan config. -type VLANConfig struct { - PluginType string `json:"type"` - Master string `json:"master"` - IPAM ipamConfig `json:"ipam"` - MTU int `json:"mtu,omitempty"` - Mode string `json:"mode,omitempty"` - Capabilities map[string]bool `json:"capabilities,omitempty"` -} - -// firewallConfig describes the firewall plugin. -type firewallConfig struct { - PluginType string `json:"type"` - Backend string `json:"backend"` - IngressPolicy string `json:"ingressPolicy,omitempty"` -} - -// tuningConfig describes the tuning plugin. -type tuningConfig struct { - PluginType string `json:"type"` -} - -// 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"` -} - -// ncList describes a generic map. -type ncList map[string]any - -// newNcList creates a generic map of values with string -// keys and adds in version and network name. -func newNcList(name, version string, labels, options map[string]string) ncList { - n := ncList{} - n["cniVersion"] = version - n["name"] = name - args := map[string]map[string]string{} - if len(labels) > 0 { - args[podmanLabelKey] = labels - } - if len(options) > 0 { - args[podmanOptionsKey] = options - } - if len(args) > 0 { - n["args"] = args - } - return n -} - -// newHostLocalBridge creates a new LocalBridge for host-local. -func newHostLocalBridge(name string, isGateWay, ipMasq bool, mtu, vlan int, ipamConf *ipamConfig) *hostLocalBridge { - bridge := hostLocalBridge{ - PluginType: "bridge", - BrName: name, - IsGW: isGateWay, - IPMasq: ipMasq, - MTU: mtu, - HairpinMode: true, - Vlan: vlan, - } - if ipamConf != nil { - bridge.IPAM = *ipamConf - // if we use host-local set the ips cap to ensure we can set static ips via runtime config - if ipamConf.PluginType == types.HostLocalIPAMDriver { - bridge.Capabilities = map[string]bool{"ips": true} - } - } - return &bridge -} - -// newIPAMHostLocalConf creates a new IPAMHostLocal configuration. -func newIPAMHostLocalConf(routes []ipamRoute, ipamRanges [][]ipamLocalHostRangeConf) ipamConfig { - ipamConf := ipamConfig{ - PluginType: "host-local", - Routes: routes, - } - - ipamConf.Ranges = ipamRanges - return ipamConf -} - -// newIPAMLocalHostRange create a new IPAM range. -func newIPAMLocalHostRange(subnet types.IPNet, leaseRange *types.LeaseRange, gw net.IP) *ipamLocalHostRangeConf { - hostRange := &ipamLocalHostRangeConf{ - Subnet: subnet.String(), - } - - // a user provided a range, we add it here - if leaseRange != nil { - if leaseRange.StartIP != nil { - hostRange.RangeStart = leaseRange.StartIP.String() - } - if leaseRange.EndIP != nil { - hostRange.RangeEnd = leaseRange.EndIP.String() - } - } - - if gw != nil { - hostRange.Gateway = gw.String() - } - return hostRange -} - -// newIPAMRoute creates a new IPAM route configuration -// nolint:interfacer -func newIPAMRoute(r *net.IPNet) ipamRoute { - 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 { - return portMapConfig{ - PluginType: "portmap", - Capabilities: map[string]bool{"portMappings": true}, - } -} - -// newFirewallPlugin creates a generic firewall plugin. -func newFirewallPlugin(isolate bool) firewallConfig { - fw := firewallConfig{ - PluginType: "firewall", - } - if isolate { - fw.IngressPolicy = ingressPolicySameBridge - } - return fw -} - -// 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 { - return dnsNameConfig{ - PluginType: "dnsname", - DomainName: domainName, - Capabilities: map[string]bool{"aliases": true}, - } -} - -// hasDNSNamePlugin looks to see if the dnsname cni plugin is present. -func hasDNSNamePlugin(paths []string) bool { - for _, p := range paths { - if err := fileutils.Exists(filepath.Join(p, "dnsname")); err == nil { - return true - } - } - return false -} - -// newVLANPlugin creates a macvlanconfig with a given device name. -func newVLANPlugin(pluginType, device, mode string, mtu int, ipam *ipamConfig) VLANConfig { - m := VLANConfig{ - PluginType: pluginType, - } - if ipam != nil { - m.IPAM = *ipam - } - if mtu > 0 { - m.MTU = mtu - } - if len(mode) > 0 { - m.Mode = mode - } - // CNI is supposed to use the default route if a - // parent device is not provided - if len(device) > 0 { - m.Master = device - } - caps := make(map[string]bool) - caps["ips"] = true - // if we use host-local set the ips cap to ensure we can set static ips via runtime config - if m.IPAM.PluginType == types.HostLocalIPAMDriver { - m.Capabilities = caps - } - return m -} diff --git a/common/libnetwork/cni/config.go b/common/libnetwork/cni/config.go deleted file mode 100644 index a8060bd723..0000000000 --- a/common/libnetwork/cni/config.go +++ /dev/null @@ -1,246 +0,0 @@ -//go:build (linux || freebsd) && cni - -package cni - -import ( - "errors" - "fmt" - "net" - "os" - "slices" - - "github.com/sirupsen/logrus" - internalutil "go.podman.io/common/libnetwork/internal/util" - "go.podman.io/common/libnetwork/types" -) - -func (n *cniNetwork) NetworkUpdate(_ string, _ types.NetworkUpdateOptions) error { - return fmt.Errorf("NetworkUpdate is not supported for backend CNI: %w", types.ErrInvalidArg) -} - -// NetworkCreate will take a partial filled Network and fill the -// missing fields. It creates the Network and returns the full Network. -func (n *cniNetwork) NetworkCreate(net types.Network, options *types.NetworkCreateOptions) (types.Network, error) { - n.lock.Lock() - defer n.lock.Unlock() - err := n.loadNetworks() - if err != nil { - return types.Network{}, err - } - network, err := n.networkCreate(&net, false) - if err != nil { - if options != nil && options.IgnoreIfExists && errors.Is(err, types.ErrNetworkExists) { - if network, ok := n.networks[net.Name]; ok { - return *network.libpodNet, nil - } - } - return types.Network{}, err - } - // add the new network to the map - n.networks[network.libpodNet.Name] = network - return *network.libpodNet, nil -} - -// networkCreate will fill out the given network struct and return the new network entry. -// If defaultNet is true it will not validate against used subnets and it will not write the cni config to disk. -func (n *cniNetwork) networkCreate(newNetwork *types.Network, defaultNet bool) (*network, error) { - if len(newNetwork.NetworkDNSServers) > 0 { - return nil, fmt.Errorf("NetworkDNSServers cannot be configured for backend CNI: %w", types.ErrInvalidArg) - } - // if no driver is set use the default one - if newNetwork.Driver == "" { - newNetwork.Driver = types.DefaultNetworkDriver - } - - // FIXME: Should we use a different type for network create without the ID field? - // the caller is not allowed to set a specific ID - if newNetwork.ID != "" { - return nil, fmt.Errorf("ID can not be set for network create: %w", types.ErrInvalidArg) - } - - err := internalutil.CommonNetworkCreate(n, newNetwork) - if err != nil { - return nil, err - } - - err = validateIPAMDriver(newNetwork) - if err != nil { - return nil, err - } - - // Only get the used networks for validation if we do not create the default network. - // The default network should not be validated against used subnets, we have to ensure - // that this network can always be created even when a subnet is already used on the host. - // This could happen if you run a container on this net, then the cni interface will be - // created on the host and "block" this subnet from being used again. - // Therefore the next podman command tries to create the default net again and it would - // fail because it thinks the network is used on the host. - var usedNetworks []*net.IPNet - if !defaultNet && newNetwork.Driver == types.BridgeNetworkDriver { - usedNetworks, err = internalutil.GetUsedSubnets(n) - if err != nil { - return nil, err - } - } - - switch newNetwork.Driver { - case types.BridgeNetworkDriver: - internalutil.MapDockerBridgeDriverOptions(newNetwork) - err = internalutil.CreateBridge(n, newNetwork, usedNetworks, n.defaultsubnetPools, true) - if err != nil { - return nil, err - } - case types.MacVLANNetworkDriver, types.IPVLANNetworkDriver: - err = createIPMACVLAN(newNetwork) - if err != nil { - return nil, err - } - default: - return nil, fmt.Errorf("unsupported driver %s: %w", newNetwork.Driver, types.ErrInvalidArg) - } - - err = internalutil.ValidateSubnets(newNetwork, !newNetwork.Internal, usedNetworks) - if err != nil { - return nil, err - } - - // generate the network ID - newNetwork.ID = getNetworkIDFromName(newNetwork.Name) - - // when we do not have ipam we must disable dns - internalutil.IpamNoneDisableDNS(newNetwork) - - // FIXME: Should this be a hard error? - if newNetwork.DNSEnabled && newNetwork.Internal && hasDNSNamePlugin(n.cniPluginDirs) { - logrus.Warnf("dnsname and internal networks are incompatible. dnsname plugin not configured for network %s", newNetwork.Name) - newNetwork.DNSEnabled = false - } - - cniConf, path, err := n.createCNIConfigListFromNetwork(newNetwork, !defaultNet) - if err != nil { - return nil, err - } - return &network{cniNet: cniConf, libpodNet: newNetwork, filename: path}, nil -} - -// NetworkRemove will remove the Network with the given name or ID. -// It does not ensure that the network is unused. -func (n *cniNetwork) NetworkRemove(nameOrID string) error { - n.lock.Lock() - defer n.lock.Unlock() - err := n.loadNetworks() - if err != nil { - return err - } - - network, err := n.getNetwork(nameOrID) - if err != nil { - return err - } - - // Removing the default network is not allowed. - if network.libpodNet.Name == n.defaultNetwork { - return fmt.Errorf("default network %s cannot be removed", n.defaultNetwork) - } - - // Remove the bridge network interface on the host. - if network.libpodNet.Driver == types.BridgeNetworkDriver { - deleteLink(network.libpodNet.NetworkInterface) - } - - file := network.filename - delete(n.networks, network.libpodNet.Name) - - // make sure to not error for ErrNotExist - if err := os.Remove(file); err != nil && !errors.Is(err, os.ErrNotExist) { - return err - } - return nil -} - -// NetworkList will return all known Networks. Optionally you can -// supply a list of filter functions. Only if a network matches all -// functions it is returned. -func (n *cniNetwork) NetworkList(filters ...types.FilterFunc) ([]types.Network, error) { - n.lock.Lock() - defer n.lock.Unlock() - err := n.loadNetworks() - if err != nil { - return nil, err - } - - networks := make([]types.Network, 0, len(n.networks)) -outer: - for _, net := range n.networks { - for _, filter := range filters { - // All filters have to match, if one does not match we can skip to the next network. - if !filter(*net.libpodNet) { - continue outer - } - } - networks = append(networks, *net.libpodNet) - } - return networks, nil -} - -// NetworkInspect will return the Network with the given name or ID. -func (n *cniNetwork) NetworkInspect(nameOrID string) (types.Network, error) { - n.lock.Lock() - defer n.lock.Unlock() - err := n.loadNetworks() - if err != nil { - return types.Network{}, err - } - - network, err := n.getNetwork(nameOrID) - if err != nil { - return types.Network{}, err - } - return *network.libpodNet, nil -} - -func createIPMACVLAN(network *types.Network) error { - if network.NetworkInterface != "" { - interfaceNames, err := internalutil.GetLiveNetworkNames() - if err != nil { - return err - } - if !slices.Contains(interfaceNames, network.NetworkInterface) { - return fmt.Errorf("parent interface %s does not exist", network.NetworkInterface) - } - } - - switch network.IPAMOptions[types.Driver] { - // set default - case "": - if len(network.Subnets) == 0 { - // if no subnets and no driver choose dhcp - network.IPAMOptions[types.Driver] = types.DHCPIPAMDriver - } else { - network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver - } - case types.HostLocalIPAMDriver: - if len(network.Subnets) == 0 { - return errors.New("host-local ipam driver set but no subnets are given") - } - } - - if network.IPAMOptions[types.Driver] == types.DHCPIPAMDriver && network.Internal { - return errors.New("internal is not supported with macvlan and dhcp ipam driver") - } - return nil -} - -func validateIPAMDriver(n *types.Network) error { - ipamDriver := n.IPAMOptions[types.Driver] - switch ipamDriver { - case "", types.HostLocalIPAMDriver: - case types.DHCPIPAMDriver, types.NoneIPAMDriver: - if len(n.Subnets) > 0 { - return fmt.Errorf("%s ipam driver is set but subnets are given", ipamDriver) - } - default: - return fmt.Errorf("unsupported ipam driver %q", ipamDriver) - } - return nil -} diff --git a/common/libnetwork/cni/config_freebsd.go b/common/libnetwork/cni/config_freebsd.go deleted file mode 100644 index ddee6d2e01..0000000000 --- a/common/libnetwork/cni/config_freebsd.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build (linux || freebsd) && cni - -package cni - -import ( - "os/exec" - - "github.com/sirupsen/logrus" -) - -func deleteLink(name string) { - if output, err := exec.Command("ifconfig", name, "destroy").CombinedOutput(); err != nil { - // only log the error, it is not fatal - logrus.Infof("Failed to remove network interface %s: %v: %s", name, err, output) - } -} diff --git a/common/libnetwork/cni/config_linux.go b/common/libnetwork/cni/config_linux.go deleted file mode 100644 index efc920614c..0000000000 --- a/common/libnetwork/cni/config_linux.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build (linux || freebsd) && cni - -package cni - -import ( - "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" -) - -func deleteLink(name string) { - link, err := netlink.LinkByName(name) - if err == nil { - err = netlink.LinkDel(link) - // only log the error, it is not fatal - if err != nil { - logrus.Infof("Failed to remove network interface %s: %v", name, err) - } - } -} diff --git a/common/libnetwork/cni/config_test.go b/common/libnetwork/cni/config_test.go deleted file mode 100644 index 9b11befe12..0000000000 --- a/common/libnetwork/cni/config_test.go +++ /dev/null @@ -1,1749 +0,0 @@ -//go:build (linux || freebsd) && cni - -package cni_test - -import ( - "bytes" - "fmt" - "net" - "os" - "path/filepath" - "time" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - gomegaTypes "github.com/onsi/gomega/types" - "github.com/sirupsen/logrus" - "go.podman.io/common/libnetwork/types" - "go.podman.io/common/libnetwork/util" -) - -var _ = Describe("Config", func() { - var ( - libpodNet types.ContainerNetwork - cniConfDir string - logBuffer bytes.Buffer - ) - - BeforeEach(func() { - t := GinkgoT() - cniConfDir = t.TempDir() - logBuffer = bytes.Buffer{} - logrus.SetOutput(&logBuffer) - logrus.SetLevel(logrus.InfoLevel) - DeferCleanup(func() { - logrus.SetLevel(logrus.InfoLevel) - }) - }) - - JustBeforeEach(func() { - var err error - libpodNet, err = getNetworkInterface(cniConfDir) - if err != nil { - Fail("Failed to create NewCNINetworkInterface") - } - }) - - Context("basic network config tests", func() { - It("check default network config exists", func() { - networks, err := libpodNet.NetworkList() - Expect(err).ToNot(HaveOccurred()) - Expect(networks).To(HaveLen(1)) - Expect(networks[0].Name).To(Equal("podman")) - Expect(networks[0].Driver).To(Equal("bridge")) - Expect(networks[0].NetworkInterface).To(Equal("cni-podman0")) - Expect(networks[0].Created.Before(time.Now())).To(BeTrue()) - Expect(networks[0].Subnets).To(HaveLen(1)) - Expect(networks[0].Subnets[0].Subnet.String()).To(Equal("10.88.0.0/16")) - Expect(networks[0].Subnets[0].Gateway.String()).To(Equal("10.88.0.1")) - Expect(networks[0].Subnets[0].LeaseRange).To(BeNil()) - Expect(networks[0].IPAMOptions).To(HaveKeyWithValue("driver", "host-local")) - Expect(networks[0].Options).To(BeEmpty()) - Expect(networks[0].Labels).To(BeEmpty()) - Expect(networks[0].DNSEnabled).To(BeFalse()) - Expect(networks[0].Internal).To(BeFalse()) - }) - - It("basic network create, inspect and remove", func() { - // Because we get the time from the file create timestamp there is small precision - // loss so lets remove 500 milliseconds to make sure this test does not flake. - now := time.Now().Add(-500 * time.Millisecond) - network := types.Network{} - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - path := filepath.Join(cniConfDir, network1.Name+".conflist") - Expect(path).To(BeARegularFile()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Labels).To(BeEmpty()) - Expect(network1.Options).To(BeEmpty()) - Expect(network1.IPAMOptions).ToNot(BeEmpty()) - Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "host-local")) - Expect(network1.Created.After(now)).To(BeTrue()) - Expect(network1.Subnets).To(HaveLen(1)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal("10.89.0.0/24")) - Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.89.0.1")) - Expect(network1.Subnets[0].LeaseRange).To(BeNil()) - Expect(network1.DNSEnabled).To(BeFalse()) - Expect(network1.Internal).To(BeFalse()) - - // inspect by name - network2, err := libpodNet.NetworkInspect(network1.Name) - Expect(err).ToNot(HaveOccurred()) - Expect(network2).To(Equal(network1)) - - // inspect by ID - network2, err = libpodNet.NetworkInspect(network1.ID) - Expect(err).ToNot(HaveOccurred()) - Expect(network2).To(Equal(network1)) - - // inspect by partial ID - network2, err = libpodNet.NetworkInspect(network1.ID[:10]) - Expect(err).ToNot(HaveOccurred()) - Expect(network2).To(Equal(network1)) - - // create a new interface to force a config load from disk - libpodNet, err = getNetworkInterface(cniConfDir) - Expect(err).ToNot(HaveOccurred()) - - network2, err = libpodNet.NetworkInspect(network1.Name) - Expect(err).ToNot(HaveOccurred()) - Expect(network2).To(Equal(network1)) - - err = libpodNet.NetworkRemove(network1.Name) - Expect(err).ToNot(HaveOccurred()) - Expect(path).ToNot(BeARegularFile()) - - _, err = libpodNet.NetworkInspect(network1.Name) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("network not found")) - }) - - It("create two networks", func() { - // remove the dir here since we do not expect it to exists in the real context as well - // the backend will create it for us - os.RemoveAll(cniConfDir) - - network := types.Network{} - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.Subnets).To(HaveLen(1)) - - network = types.Network{} - network2, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network2.Name).ToNot(Equal(network1.Name)) - Expect(network2.ID).ToNot(Equal(network1.ID)) - Expect(network2.NetworkInterface).ToNot(Equal(network1.NetworkInterface)) - Expect(network2.Subnets).To(HaveLen(1)) - Expect(network2.Subnets[0].Subnet.Contains(network1.Subnets[0].Subnet.IP)).To(BeFalse()) - }) - - It("fail when creating two networks with the same name", func() { - network := types.Network{} - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.Subnets).To(HaveLen(1)) - - network = types.Network{Name: network1.Name} - _, err = libpodNet.NetworkCreate(network, nil) - Expect(err).To(MatchError(types.ErrNetworkExists)) - }) - - It("return the same network when creating two networks with the same name and ignore", func() { - network := types.Network{} - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.Subnets).To(HaveLen(1)) - - network = types.Network{Name: network1.Name} - network2, err := libpodNet.NetworkCreate(network, &types.NetworkCreateOptions{IgnoreIfExists: true}) - Expect(err).ToNot(HaveOccurred()) - Expect(network2).To(Equal(network1)) - }) - - It("create network with NetworDNSServers with DNSEnabled=false", func() { - network := types.Network{ - NetworkDNSServers: []string{"8.8.8.8", "3.3.3.3"}, - DNSEnabled: false, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`NetworkDNSServers cannot be configured for backend CNI`)) - }) - - It("create bridge config", func() { - network := types.Network{Driver: "bridge"} - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(filepath.Join(cniConfDir, network1.Name+".conflist")).To(BeARegularFile()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Labels).To(BeEmpty()) - Expect(network1.Options).To(BeEmpty()) - Expect(network1.IPAMOptions).ToNot(BeEmpty()) - Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "host-local")) - Expect(network1.Subnets).To(HaveLen(1)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal("10.89.0.0/24")) - Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.89.0.1")) - Expect(network1.Subnets[0].LeaseRange).To(BeNil()) - Expect(network1.DNSEnabled).To(BeFalse()) - Expect(network1.Internal).To(BeFalse()) - }) - - It("create bridge config with com.docker.network.bridge.name", func() { - network := types.Network{ - Driver: "bridge", - Options: map[string]string{ - "com.docker.network.bridge.name": "foo", - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(filepath.Join(cniConfDir, network1.Name+".conflist")).To(BeARegularFile()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).To(Equal("foo")) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Labels).To(BeEmpty()) - Expect(network1.Options).To(BeEmpty()) - Expect(network1.IPAMOptions).ToNot(BeEmpty()) - Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "host-local")) - Expect(network1.Subnets).To(HaveLen(1)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal("10.89.0.0/24")) - Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.89.0.1")) - Expect(network1.Subnets[0].LeaseRange).To(BeNil()) - Expect(network1.DNSEnabled).To(BeFalse()) - Expect(network1.Internal).To(BeFalse()) - }) - - It("create bridge config with none ipam driver", func() { - network := types.Network{ - Driver: "bridge", - IPAMOptions: map[string]string{ - "driver": "none", - }, - DNSEnabled: true, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.DNSEnabled).To(BeFalse()) - Expect(network1.IPAMOptions).ToNot(BeEmpty()) - Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "none")) - Expect(network1.Subnets).To(BeEmpty()) - - // reload configs from disk - libpodNet, err = getNetworkInterface(cniConfDir) - Expect(err).ToNot(HaveOccurred()) - - network2, err := libpodNet.NetworkInspect(network1.Name) - Expect(err).ToNot(HaveOccurred()) - Expect(network2).To(Equal(network1)) - }) - - It("create bridge config with none ipam driver and subnets", func() { - subnet := "10.1.0.0/24" - n, _ := types.ParseCIDR(subnet) - network := types.Network{ - Driver: "bridge", - IPAMOptions: map[string]string{ - "driver": "none", - }, - Subnets: []types.Subnet{ - {Subnet: n}, - }, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("none ipam driver is set but subnets are given")) - }) - - It("create bridge config with dhcp ipam driver", func() { - network := types.Network{ - Driver: "bridge", - IPAMOptions: map[string]string{ - "driver": "dhcp", - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.IPAMOptions).ToNot(BeEmpty()) - Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) - Expect(network1.Subnets).To(BeEmpty()) - - // reload configs from disk - libpodNet, err = getNetworkInterface(cniConfDir) - Expect(err).ToNot(HaveOccurred()) - - network2, err := libpodNet.NetworkInspect(network1.Name) - Expect(err).ToNot(HaveOccurred()) - Expect(network2).To(Equal(network1)) - }) - - It("create bridge config with none hdcp driver and subnets", func() { - subnet := "10.1.0.0/24" - n, _ := types.ParseCIDR(subnet) - network := types.Network{ - Driver: "bridge", - IPAMOptions: map[string]string{ - "driver": "dhcp", - }, - Subnets: []types.Subnet{ - {Subnet: n}, - }, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("dhcp ipam driver is set but subnets are given")) - }) - - It("create bridge with same name should fail", func() { - network := types.Network{ - Driver: "bridge", - NetworkInterface: "cni-podman2", - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).To(Equal("cni-podman2")) - Expect(network1.Driver).To(Equal("bridge")) - - _, err = libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("bridge name cni-podman2 already in use")) - }) - - It("create macvlan config", func() { - network := types.Network{Driver: "macvlan"} - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(filepath.Join(cniConfDir, network1.Name+".conflist")).To(BeARegularFile()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("macvlan")) - Expect(network1.Labels).To(BeEmpty()) - Expect(network1.Options).To(BeEmpty()) - Expect(network1.IPAMOptions).ToNot(BeEmpty()) - Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) - Expect(network1.Subnets).To(BeEmpty()) - Expect(network1.DNSEnabled).To(BeFalse()) - Expect(network1.Internal).To(BeFalse()) - }) - - It("create macvlan config with device", func() { - network := types.Network{ - Driver: "macvlan", - NetworkInterface: "lo", - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - path := filepath.Join(cniConfDir, network1.Name+".conflist") - Expect(path).To(BeARegularFile()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("macvlan")) - Expect(network1.Labels).To(BeEmpty()) - Expect(network1.Options).To(BeEmpty()) - Expect(network1.Subnets).To(BeEmpty()) - Expect(network1.DNSEnabled).To(BeFalse()) - Expect(network1.Internal).To(BeFalse()) - Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) - grepInFile(path, `"type": "macvlan"`) - grepInFile(path, `"master": "lo"`) - grepInFile(path, `"type": "dhcp"`) - }) - - It("create macvlan config with subnet", func() { - subnet := "10.1.0.0/24" - n, _ := types.ParseCIDR(subnet) - network := types.Network{ - Driver: "macvlan", - Subnets: []types.Subnet{ - {Subnet: n}, - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - path := filepath.Join(cniConfDir, network1.Name+".conflist") - Expect(path).To(BeARegularFile()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("macvlan")) - Expect(network1.Labels).To(BeEmpty()) - Expect(network1.Options).To(BeEmpty()) - Expect(network1.Subnets).To(HaveLen(1)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) - Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.1.0.1")) - Expect(network1.Subnets[0].LeaseRange).To(BeNil()) - Expect(network1.DNSEnabled).To(BeFalse()) - Expect(network1.Internal).To(BeFalse()) - Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "host-local")) - grepInFile(path, `"type": "host-local"`) - }) - - // https://github.com/containers/podman/issues/12971 - It("create macvlan with a used subnet", func() { - subnet := "127.0.0.0/8" - n, _ := types.ParseCIDR(subnet) - network := types.Network{ - Driver: "macvlan", - Subnets: []types.Subnet{ - {Subnet: n}, - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - path := filepath.Join(cniConfDir, network1.Name+".conflist") - Expect(path).To(BeARegularFile()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("macvlan")) - Expect(network1.Subnets).To(HaveLen(1)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) - Expect(network1.Subnets[0].Gateway.String()).To(Equal("127.0.0.1")) - Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "host-local")) - grepInFile(path, `"type": "host-local"`) - }) - - It("create ipvlan config with subnet", func() { - subnet := "10.1.0.0/24" - n, _ := types.ParseCIDR(subnet) - network := types.Network{ - Driver: "ipvlan", - Subnets: []types.Subnet{ - {Subnet: n}, - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - path := filepath.Join(cniConfDir, network1.Name+".conflist") - Expect(path).To(BeARegularFile()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("ipvlan")) - Expect(network1.Labels).To(BeEmpty()) - Expect(network1.Options).To(BeEmpty()) - Expect(network1.Subnets).To(HaveLen(1)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) - Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.1.0.1")) - Expect(network1.Subnets[0].LeaseRange).To(BeNil()) - Expect(network1.DNSEnabled).To(BeFalse()) - Expect(network1.Internal).To(BeFalse()) - Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "host-local")) - grepInFile(path, `"type": "host-local"`) - }) - - It("create macvlan config with mode", func() { - for _, mode := range []string{"bridge", "private", "vepa", "passthru"} { - network := types.Network{ - Driver: "macvlan", - Options: map[string]string{ - types.ModeOption: mode, - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - path := filepath.Join(cniConfDir, network1.Name+".conflist") - Expect(path).To(BeARegularFile()) - Expect(network1.Driver).To(Equal("macvlan")) - Expect(network1.Options).To(HaveKeyWithValue("mode", mode)) - Expect(network1.IPAMOptions).ToNot(BeEmpty()) - Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) - grepInFile(path, `"mode": "`+mode+`"`) - } - }) - - It("create macvlan config with invalid mode", func() { - network := types.Network{ - Driver: "macvlan", - Options: map[string]string{ - types.ModeOption: "test", - }, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`unknown macvlan mode "test"`)) - }) - - It("create macvlan config with invalid device", func() { - network := types.Network{ - Driver: "macvlan", - NetworkInterface: "idonotexists", - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("parent interface idonotexists does not exist")) - }) - - It("create macvlan config with internal and dhcp should fail", func() { - subnet := "10.1.0.0/24" - n, _ := types.ParseCIDR(subnet) - network := types.Network{ - Driver: "macvlan", - Internal: true, - Subnets: []types.Subnet{ - {Subnet: n}, - }, - } - net1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(net1.Internal).To(BeTrue()) - path := filepath.Join(cniConfDir, net1.Name+".conflist") - Expect(path).To(BeARegularFile()) - grepNotFile(path, `"0.0.0.0/0"`) - }) - - It("create macvlan config with internal and subnet should not fail", func() { - network := types.Network{ - Driver: "macvlan", - Internal: true, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("internal is not supported with macvlan")) - }) - - for _, driver := range []string{"macvlan", "ipvlan"} { - It(fmt.Sprintf("create %s config with none ipam driver", driver), func() { - network := types.Network{ - Driver: driver, - IPAMOptions: map[string]string{ - "driver": "none", - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Driver).To(Equal(driver)) - Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "none")) - Expect(network1.Subnets).To(BeEmpty()) - - // reload configs from disk - libpodNet, err = getNetworkInterface(cniConfDir) - Expect(err).ToNot(HaveOccurred()) - - network2, err := libpodNet.NetworkInspect(network1.Name) - Expect(err).ToNot(HaveOccurred()) - Expect(network2).To(Equal(network1)) - }) - } - - It("create ipvlan config with mode", func() { - for _, mode := range []string{"l2", "l3", "l3s"} { - network := types.Network{ - Driver: "ipvlan", - Options: map[string]string{ - types.ModeOption: mode, - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - path := filepath.Join(cniConfDir, network1.Name+".conflist") - Expect(path).To(BeARegularFile()) - Expect(network1.Driver).To(Equal("ipvlan")) - Expect(network1.Options).To(HaveKeyWithValue("mode", mode)) - Expect(network1.IPAMOptions).ToNot(BeEmpty()) - Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) - grepInFile(path, `"mode": "`+mode+`"`) - - // reload configs from disk - libpodNet, err = getNetworkInterface(cniConfDir) - Expect(err).ToNot(HaveOccurred()) - - network2, err := libpodNet.NetworkInspect(network1.Name) - Expect(err).ToNot(HaveOccurred()) - Expect(network2).To(Equal(network1)) - } - }) - - It("create ipvlan config with invalid mode", func() { - network := types.Network{ - Driver: "ipvlan", - Options: map[string]string{ - types.ModeOption: "test", - }, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`unknown ipvlan mode "test"`)) - }) - - It("create bridge with subnet", func() { - subnet := "10.0.0.0/24" - n, _ := types.ParseCIDR(subnet) - - network := types.Network{ - Driver: "bridge", - Subnets: []types.Subnet{ - {Subnet: n}, - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Subnets).To(HaveLen(1)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) - Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1")) - Expect(network1.Subnets[0].LeaseRange).To(BeNil()) - }) - - It("create bridge with ipv6 subnet", func() { - subnet := "fdcc::/64" - n, _ := types.ParseCIDR(subnet) - - network := types.Network{ - Driver: "bridge", - Subnets: []types.Subnet{ - {Subnet: n}, - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.IPv6Enabled).To(BeTrue()) - Expect(network1.Subnets).To(HaveLen(1)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) - Expect(network1.Subnets[0].Gateway.String()).To(Equal("fdcc::1")) - Expect(network1.Subnets[0].LeaseRange).To(BeNil()) - - // reload configs from disk - libpodNet, err = getNetworkInterface(cniConfDir) - Expect(err).ToNot(HaveOccurred()) - // check the networks are identical - network2, err := libpodNet.NetworkInspect(network1.Name) - Expect(err).ToNot(HaveOccurred()) - Expect(network1).To(Equal(network2)) - }) - - It("create bridge with ipv6 enabled", func() { - network := types.Network{ - Driver: "bridge", - IPv6Enabled: true, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Subnets).To(HaveLen(2)) - Expect(network1.Subnets[0].Subnet.String()).To(ContainSubstring(".0/24")) - Expect(network1.Subnets[0].Gateway).ToNot(BeNil()) - Expect(network1.Subnets[0].LeaseRange).To(BeNil()) - Expect(network1.Subnets[1].Subnet.String()).To(ContainSubstring("::/64")) - Expect(network1.Subnets[1].Gateway).ToNot(BeNil()) - Expect(network1.Subnets[1].LeaseRange).To(BeNil()) - }) - - It("create bridge with ipv6 enabled and ipv4 subnet", func() { - subnet := "10.100.0.0/24" - n, _ := types.ParseCIDR(subnet) - - network := types.Network{ - Driver: "bridge", - Subnets: []types.Subnet{ - {Subnet: n}, - }, - IPv6Enabled: true, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Subnets).To(HaveLen(2)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) - Expect(network1.Subnets[0].Gateway).ToNot(BeNil()) - Expect(network1.Subnets[0].LeaseRange).To(BeNil()) - Expect(network1.Subnets[1].Subnet.String()).To(ContainSubstring("::/64")) - Expect(network1.Subnets[1].Gateway).ToNot(BeNil()) - Expect(network1.Subnets[1].LeaseRange).To(BeNil()) - }) - - It("create bridge with ipv6 enabled and ipv6 subnet", func() { - subnet := "fd66::/64" - n, _ := types.ParseCIDR(subnet) - - network := types.Network{ - Driver: "bridge", - Subnets: []types.Subnet{ - {Subnet: n}, - }, - IPv6Enabled: true, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Subnets).To(HaveLen(2)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) - Expect(network1.Subnets[0].Gateway).ToNot(BeNil()) - Expect(network1.Subnets[0].LeaseRange).To(BeNil()) - Expect(network1.Subnets[1].Subnet.String()).To(ContainSubstring(".0/24")) - Expect(network1.Subnets[1].Gateway).ToNot(BeNil()) - Expect(network1.Subnets[1].LeaseRange).To(BeNil()) - }) - - It("create bridge with ipv6 enabled and ipv4+ipv6 subnet", func() { - subnet1 := "10.100.0.0/24" - n1, _ := types.ParseCIDR(subnet1) - subnet2 := "fd66::/64" - n2, _ := types.ParseCIDR(subnet2) - - network := types.Network{ - Driver: "bridge", - Subnets: []types.Subnet{ - {Subnet: n1}, {Subnet: n2}, - }, - IPv6Enabled: true, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Subnets).To(HaveLen(2)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet1)) - Expect(network1.Subnets[0].Gateway).ToNot(BeNil()) - Expect(network1.Subnets[0].LeaseRange).To(BeNil()) - Expect(network1.Subnets[1].Subnet.String()).To(Equal(subnet2)) - Expect(network1.Subnets[1].Gateway).ToNot(BeNil()) - Expect(network1.Subnets[1].LeaseRange).To(BeNil()) - }) - - It("create bridge with ipv6 enabled and two ipv4 subnets", func() { - subnet1 := "10.100.0.0/24" - n1, _ := types.ParseCIDR(subnet1) - subnet2 := "10.200.0.0/24" - n2, _ := types.ParseCIDR(subnet2) - - network := types.Network{ - Driver: "bridge", - Subnets: []types.Subnet{ - {Subnet: n1}, {Subnet: n2}, - }, - IPv6Enabled: true, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Subnets).To(HaveLen(3)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet1)) - Expect(network1.Subnets[0].Gateway).ToNot(BeNil()) - Expect(network1.Subnets[0].LeaseRange).To(BeNil()) - Expect(network1.Subnets[1].Subnet.String()).To(Equal(subnet2)) - Expect(network1.Subnets[1].Gateway).ToNot(BeNil()) - Expect(network1.Subnets[1].LeaseRange).To(BeNil()) - Expect(network1.Subnets[2].Subnet.String()).To(ContainSubstring("::/64")) - Expect(network1.Subnets[2].Gateway).ToNot(BeNil()) - Expect(network1.Subnets[2].LeaseRange).To(BeNil()) - }) - - It("create bridge with subnet and gateway", func() { - subnet := "10.0.0.5/24" - n, _ := types.ParseCIDR(subnet) - gateway := "10.0.0.50" - g := net.ParseIP(gateway) - network := types.Network{ - Driver: "bridge", - Subnets: []types.Subnet{ - {Subnet: n, Gateway: g}, - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Subnets).To(HaveLen(1)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal("10.0.0.0/24")) - Expect(network1.Subnets[0].Gateway.String()).To(Equal(gateway)) - Expect(network1.Subnets[0].LeaseRange).To(BeNil()) - }) - - It("create bridge with subnet and gateway not in the same subnet", func() { - subnet := "10.0.0.0/24" - n, _ := types.ParseCIDR(subnet) - gateway := "10.10.0.50" - g := net.ParseIP(gateway) - network := types.Network{ - Driver: "bridge", - Subnets: []types.Subnet{ - {Subnet: n, Gateway: g}, - }, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("not in subnet")) - }) - - It("create bridge with subnet and lease range", func() { - subnet := "10.0.0.0/24" - n, _ := types.ParseCIDR(subnet) - startIP := "10.0.0.10" - network := types.Network{ - Driver: "bridge", - Subnets: []types.Subnet{ - {Subnet: n, LeaseRange: &types.LeaseRange{ - StartIP: net.ParseIP(startIP), - }}, - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Subnets).To(HaveLen(1)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) - Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1")) - Expect(network1.Subnets[0].LeaseRange.StartIP.String()).To(Equal(startIP)) - - err = libpodNet.NetworkRemove(network1.Name) - Expect(err).ToNot(HaveOccurred()) - - endIP := "10.0.0.30" - network = types.Network{ - Driver: "bridge", - Subnets: []types.Subnet{ - {Subnet: n, LeaseRange: &types.LeaseRange{ - EndIP: net.ParseIP(endIP), - }}, - }, - } - network1, err = libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(filepath.Join(cniConfDir, network1.Name+".conflist")).To(BeARegularFile()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Subnets).To(HaveLen(1)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) - Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1")) - Expect(network1.Subnets[0].LeaseRange.EndIP.String()).To(Equal(endIP)) - - err = libpodNet.NetworkRemove(network1.Name) - Expect(err).ToNot(HaveOccurred()) - - network = types.Network{ - Driver: "bridge", - Subnets: []types.Subnet{ - {Subnet: n, LeaseRange: &types.LeaseRange{ - StartIP: net.ParseIP(startIP), - EndIP: net.ParseIP(endIP), - }}, - }, - } - network1, err = libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Subnets).To(HaveLen(1)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) - Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1")) - Expect(network1.Subnets[0].LeaseRange.StartIP.String()).To(Equal(startIP)) - Expect(network1.Subnets[0].LeaseRange.EndIP.String()).To(Equal(endIP)) - - // create a new interface to force a config load from disk - libpodNet, err = getNetworkInterface(cniConfDir) - Expect(err).ToNot(HaveOccurred()) - - network1, err = libpodNet.NetworkInspect(network1.Name) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).ToNot(BeEmpty()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Subnets).To(HaveLen(1)) - Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) - Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1")) - Expect(network1.Subnets[0].LeaseRange.StartIP.String()).To(Equal(startIP)) - Expect(network1.Subnets[0].LeaseRange.EndIP.String()).To(Equal(endIP)) - }) - - It("create bridge with subnet and invalid lease range", func() { - subnet := "10.0.0.0/24" - n, _ := types.ParseCIDR(subnet) - startIP := "10.0.1.2" - network := types.Network{ - Driver: "bridge", - Subnets: []types.Subnet{ - {Subnet: n, LeaseRange: &types.LeaseRange{ - StartIP: net.ParseIP(startIP), - }}, - }, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("not in subnet")) - - endIP := "10.1.1.1" - network = types.Network{ - Driver: "bridge", - Subnets: []types.Subnet{ - {Subnet: n, LeaseRange: &types.LeaseRange{ - EndIP: net.ParseIP(endIP), - }}, - }, - } - _, err = libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("not in subnet")) - }) - - It("create bridge with broken subnet", func() { - network := types.Network{ - Driver: "bridge", - Subnets: []types.Subnet{ - {Subnet: types.IPNet{}}, - }, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("subnet ip is nil")) - }) - - It("create network with name", func() { - name := "myname" - network := types.Network{ - Name: name, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).To(Equal(name)) - Expect(network1.NetworkInterface).ToNot(Equal(name)) - Expect(network1.Driver).To(Equal("bridge")) - }) - - It("create network with invalid name", func() { - name := "myname@some" - network := types.Network{ - Name: name, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - }) - - It("create network with name", func() { - name := "myname" - network := types.Network{ - Name: name, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).To(Equal(name)) - Expect(network1.NetworkInterface).ToNot(Equal(name)) - Expect(network1.Driver).To(Equal("bridge")) - }) - - It("create network with invalid name", func() { - name := "myname@some" - network := types.Network{ - Name: name, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - }) - - It("create network with interface name", func() { - name := "myname" - network := types.Network{ - NetworkInterface: name, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).ToNot(Equal(name)) - Expect(network1.NetworkInterface).To(Equal(name)) - Expect(network1.Driver).To(Equal("bridge")) - }) - - It("create network with invalid interface name", func() { - name := "myname@some" - network := types.Network{ - NetworkInterface: name, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - }) - - It("create bridge config with invalid com.docker.network.bridge.name", func() { - network := types.Network{ - Driver: "bridge", - Options: map[string]string{ - "com.docker.network.bridge.name": "myname@some", - }, - } - - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - }) - - It("create network with ID should fail", func() { - id := "17f29b073143d8cd97b5bbe492bdeffec1c5fee55cc1fe2112c8b9335f8b6121" - network := types.Network{ - ID: id, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("ID can not be set for network create")) - }) - - It("create bridge with dns", func() { - SkipIfNoDnsname() - network := types.Network{ - Driver: "bridge", - DNSEnabled: true, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.DNSEnabled).To(BeTrue()) - path := filepath.Join(cniConfDir, network1.Name+".conflist") - Expect(path).To(BeARegularFile()) - grepInFile(path, `"type": "dnsname"`) - }) - - It("create bridge with internal", func() { - network := types.Network{ - Driver: "bridge", - Internal: true, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Subnets).To(HaveLen(1)) - Expect(network1.Subnets[0].Subnet.String()).ToNot(BeEmpty()) - Expect(network1.Subnets[0].Gateway).To(BeNil()) - Expect(network1.Internal).To(BeTrue()) - path := filepath.Join(cniConfDir, network1.Name+".conflist") - Expect(path).To(BeARegularFile()) - grepNotFile(path, `"0.0.0.0/0"`) - }) - - It("create network with labels", func() { - network := types.Network{ - Labels: map[string]string{ - "key": "value", - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Labels).ToNot(BeNil()) - Expect(network1.Labels).To(ContainElement("value")) - }) - - It("create network with mtu option", func() { - network := types.Network{ - Options: map[string]string{ - types.MTUOption: "1500", - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Options).ToNot(BeNil()) - path := filepath.Join(cniConfDir, network1.Name+".conflist") - Expect(path).To(BeARegularFile()) - grepInFile(path, `"mtu": 1500,`) - Expect(network1.Options).To(HaveKeyWithValue("mtu", "1500")) - }) - - It("create network with invalid mtu option", func() { - network := types.Network{ - Options: map[string]string{ - types.MTUOption: "abc", - }, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`parsing "abc": invalid syntax`)) - - network = types.Network{ - Options: map[string]string{ - types.MTUOption: "-1", - }, - } - _, err = libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`mtu -1 is less than zero`)) - }) - - It("create network with com.docker.network.driver.mtu option", func() { - network := types.Network{ - Options: map[string]string{ - "com.docker.network.driver.mtu": "1500", - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Options).ToNot(BeNil()) - path := filepath.Join(cniConfDir, network1.Name+".conflist") - Expect(path).To(BeARegularFile()) - grepInFile(path, `"mtu": "1500"`) - Expect(network1.Options).To(HaveKeyWithValue("mtu", "1500")) - Expect(network1.Options).ToNot(HaveKeyWithValue("com.docker.network.driver.mtu", "1500")) - }) - - It("create network with invalid com.docker.network.driver.mtu option", func() { - network := types.Network{ - Options: map[string]string{ - "com.docker.network.driver.mtu": "abc", - }, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`parsing "abc": invalid syntax`)) - - network = types.Network{ - Options: map[string]string{ - "com.docker.network.driver.mtu": "-1", - }, - } - _, err = libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`mtu -1 is less than zero`)) - }) - - It("create macvlan network with mtu option", func() { - network := types.Network{ - Driver: "macvlan", - Options: map[string]string{ - types.MTUOption: "1500", - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Driver).To(Equal("macvlan")) - Expect(network1.Options).ToNot(BeNil()) - path := filepath.Join(cniConfDir, network1.Name+".conflist") - Expect(path).To(BeARegularFile()) - grepInFile(path, `"mtu": 1500`) - Expect(network1.Options).To(HaveKeyWithValue("mtu", "1500")) - }) - - It("create network with vlan option", func() { - network := types.Network{ - Options: map[string]string{ - types.VLANOption: "5", - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Options).ToNot(BeNil()) - path := filepath.Join(cniConfDir, network1.Name+".conflist") - Expect(path).To(BeARegularFile()) - grepInFile(path, `"vlan": 5,`) - Expect(network1.Options).To(HaveKeyWithValue("vlan", "5")) - }) - - It("create network with invalid vlan option", func() { - network := types.Network{ - Options: map[string]string{ - types.VLANOption: "abc", - }, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`parsing "abc": invalid syntax`)) - - network = types.Network{ - Options: map[string]string{ - types.VLANOption: "-1", - }, - } - _, err = libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`vlan ID -1 must be between 0 and 4094`)) - }) - - It("network create unsupported option", func() { - network := types.Network{Options: map[string]string{ - "someopt": "", - }} - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("unsupported network option someopt")) - }) - - It("network create unsupported driver", func() { - network := types.Network{ - Driver: "someDriver", - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("unsupported driver someDriver")) - }) - - It("network create internal and dns", func() { - SkipIfNoDnsname() - network := types.Network{ - Driver: "bridge", - Internal: true, - DNSEnabled: true, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Subnets).To(HaveLen(1)) - Expect(network1.Subnets[0].Subnet.String()).ToNot(BeEmpty()) - Expect(network1.Subnets[0].Gateway).To(BeNil()) - Expect(network1.Internal).To(BeTrue()) - // internal and dns does not work, dns should be disabled - Expect(network1.DNSEnabled).To(BeFalse()) - logString := logBuffer.String() - Expect(logString).To(ContainSubstring("dnsname and internal networks are incompatible")) - }) - - It("network inspect partial ID", func() { - network := types.Network{Name: "net4"} - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.ID).To(Equal("b44b7426c006839e7fe6f15d1faf64db58079d5233cba09b43be2257c1652cf5")) - network = types.Network{Name: "net5"} - network1, err = libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.ID).To(Equal("b67e86fb039828ad686aa13667975b9e51f192eb617044faf06cded9d31602af")) - - // Note ID is the sha256 from the name - // both net4 and net5 have an ID starting with b... - _, err = libpodNet.NetworkInspect("b") - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("more than one result for network ID")) - }) - - It("network create two with same name", func() { - network := types.Network{Name: "net"} - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Name).To(Equal("net")) - network = types.Network{Name: "net"} - _, err = libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("network name net already used")) - }) - - It("remove default network config should fail", func() { - err := libpodNet.NetworkRemove("podman") - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("default network podman cannot be removed")) - - network, err := libpodNet.NetworkInspect("podman") - Expect(err).ToNot(HaveOccurred()) - err = libpodNet.NetworkRemove(network.ID) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("default network podman cannot be removed")) - }) - - It("network create with same subnet", func() { - subnet := "10.0.0.0/24" - n, _ := types.ParseCIDR(subnet) - subnet2 := "10.10.0.0/24" - n2, _ := types.ParseCIDR(subnet2) - network := types.Network{Subnets: []types.Subnet{{Subnet: n}, {Subnet: n2}}} - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Subnets).To(HaveLen(2)) - network = types.Network{Subnets: []types.Subnet{{Subnet: n}}} - _, err = libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("subnet 10.0.0.0/24 is already used on the host or by another config")) - network = types.Network{Subnets: []types.Subnet{{Subnet: n2}}} - _, err = libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("subnet 10.10.0.0/24 is already used on the host or by another config")) - }) - - It("create network with invalid ipam driver", func() { - network := types.Network{ - Driver: "bridge", - IPAMOptions: map[string]string{ - "driver": "blah", - }, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("unsupported ipam driver \"blah\"")) - }) - - It("create network with isolate option", func() { - network := types.Network{ - Options: map[string]string{ - types.IsolateOption: "true", - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Options).ToNot(BeNil()) - path := filepath.Join(cniConfDir, network1.Name+".conflist") - Expect(path).To(BeARegularFile()) - grepInFile(path, `"ingressPolicy": "same-bridge"`) - Expect(network1.Options).To(HaveKeyWithValue("isolate", "true")) - // reload configs from disk - libpodNet, err = getNetworkInterface(cniConfDir) - Expect(err).ToNot(HaveOccurred()) - - network1, err = libpodNet.NetworkInspect(network1.Name) - Expect(err).ToNot(HaveOccurred()) - Expect(network1.Options).To(HaveKeyWithValue("isolate", "true")) - }) - - It("create network with invalid isolate option", func() { - network := types.Network{ - Options: map[string]string{ - types.IsolateOption: "123", - }, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - }) - }) - - Context("network load valid existing ones", func() { - numberOfConfigFiles := 0 - - BeforeEach(func() { - dir := "testfiles/valid" - files, err := os.ReadDir(dir) - if err != nil { - Fail("Failed to read test directory") - } - for _, file := range files { - filename := file.Name() - data, err := os.ReadFile(filepath.Join(dir, filename)) - if err != nil { - Fail("Failed to copy test files") - } - err = os.WriteFile(filepath.Join(cniConfDir, filename), data, 0o700) - if err != nil { - Fail("Failed to copy test files") - } - } - numberOfConfigFiles = len(files) - }) - - It("load networks from disk", func() { - logrus.SetLevel(logrus.WarnLevel) - nets, err := libpodNet.NetworkList() - Expect(err).ToNot(HaveOccurred()) - Expect(nets).To(HaveLen(numberOfConfigFiles)) - // test the we do not show logrus warnings/errors - logString := logBuffer.String() - Expect(logString).To(BeEmpty()) - }) - - It("load networks from disk with log level debug", func() { - logrus.SetLevel(logrus.DebugLevel) - nets, err := libpodNet.NetworkList() - Expect(err).ToNot(HaveOccurred()) - Expect(nets).To(HaveLen(numberOfConfigFiles)) - // check for the unsupported ipam plugin message - logString := logBuffer.String() - Expect(logString).ToNot(BeEmpty()) - Expect(logString).To(ContainSubstring("unsupported ipam plugin \\\"static\\\" in %s", cniConfDir+"/ipam-static.conflist")) - }) - - It("change network struct fields should not affect network struct in the backend", func() { - nets, err := libpodNet.NetworkList() - Expect(err).ToNot(HaveOccurred()) - Expect(nets).To(HaveLen(numberOfConfigFiles)) - - nets[0].Name = "myname" - nets, err = libpodNet.NetworkList() - Expect(err).ToNot(HaveOccurred()) - Expect(nets).To(HaveLen(numberOfConfigFiles)) - Expect(nets).ToNot(ContainElement(HaveNetworkName("myname"))) - - network, err := libpodNet.NetworkInspect("bridge") - Expect(err).ToNot(HaveOccurred()) - network.NetworkInterface = "abc" - - network, err = libpodNet.NetworkInspect("bridge") - Expect(err).ToNot(HaveOccurred()) - Expect(network.NetworkInterface).ToNot(Equal("abc")) - }) - - It("bridge network", func() { - network, err := libpodNet.NetworkInspect("bridge") - Expect(err).ToNot(HaveOccurred()) - Expect(network.Name).To(Equal("bridge")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.NetworkInterface).To(Equal("cni-podman9")) - Expect(network.Driver).To(Equal("bridge")) - Expect(network.Subnets).To(HaveLen(1)) - Expect(network.Subnets[0].Subnet.String()).To(Equal("10.89.8.0/24")) - Expect(network.Subnets[0].Gateway.String()).To(Equal("10.89.8.1")) - Expect(network.Subnets[0].LeaseRange).ToNot(BeNil()) - Expect(network.Subnets[0].LeaseRange.StartIP.String()).To(Equal("10.89.8.20")) - Expect(network.Subnets[0].LeaseRange.EndIP.String()).To(Equal("10.89.8.50")) - Expect(network.Internal).To(BeFalse()) - }) - - It("macvlan network", func() { - network, err := libpodNet.NetworkInspect("macvlan") - Expect(err).ToNot(HaveOccurred()) - Expect(network.Name).To(Equal("macvlan")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.NetworkInterface).To(Equal("lo")) - Expect(network.Driver).To(Equal("macvlan")) - Expect(network.Subnets).To(BeEmpty()) - // DHCP - Expect(network.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) - }) - - It("internal network", func() { - network, err := libpodNet.NetworkInspect("internal") - Expect(err).ToNot(HaveOccurred()) - Expect(network.Name).To(Equal("internal")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.NetworkInterface).To(Equal("cni-podman8")) - Expect(network.Driver).To(Equal("bridge")) - Expect(network.Subnets).To(HaveLen(1)) - Expect(network.Subnets[0].Subnet.String()).To(Equal("10.89.7.0/24")) - Expect(network.Subnets[0].Gateway).To(BeNil()) - Expect(network.Internal).To(BeTrue()) - }) - - It("bridge network with mtu", func() { - network, err := libpodNet.NetworkInspect("mtu") - Expect(err).ToNot(HaveOccurred()) - Expect(network.Name).To(Equal("mtu")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.NetworkInterface).To(Equal("cni-podman13")) - Expect(network.Driver).To(Equal("bridge")) - Expect(network.Subnets).To(HaveLen(1)) - Expect(network.Subnets[0].Subnet.String()).To(Equal("10.89.11.0/24")) - Expect(network.Subnets[0].Gateway.String()).To(Equal("10.89.11.1")) - Expect(network.Internal).To(BeFalse()) - Expect(network.Options).To(HaveLen(1)) - Expect(network.Options).To(HaveKeyWithValue("mtu", "1500")) - }) - - It("macvlan network with mtu", func() { - network, err := libpodNet.NetworkInspect("macvlan_mtu") - Expect(err).ToNot(HaveOccurred()) - Expect(network.Name).To(Equal("macvlan_mtu")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.NetworkInterface).To(Equal("lo")) - Expect(network.Driver).To(Equal("macvlan")) - Expect(network.Subnets).To(BeEmpty()) - Expect(network.Internal).To(BeFalse()) - Expect(network.Options).To(HaveLen(1)) - Expect(network.Options).To(HaveKeyWithValue("mtu", "1300")) - Expect(network.IPAMOptions).To(HaveLen(1)) - Expect(network.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) - }) - - It("bridge network with vlan", func() { - network, err := libpodNet.NetworkInspect("vlan") - Expect(err).ToNot(HaveOccurred()) - Expect(network.Name).To(Equal("vlan")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.NetworkInterface).To(Equal("cni-podman14")) - Expect(network.Driver).To(Equal("bridge")) - Expect(network.Subnets).To(HaveLen(1)) - Expect(network.Options).To(HaveLen(1)) - Expect(network.Options).To(HaveKeyWithValue("vlan", "5")) - }) - - It("bridge network with labels", func() { - network, err := libpodNet.NetworkInspect("label") - Expect(err).ToNot(HaveOccurred()) - Expect(network.Name).To(Equal("label")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.NetworkInterface).To(Equal("cni-podman15")) - Expect(network.Driver).To(Equal("bridge")) - Expect(network.Subnets).To(HaveLen(1)) - Expect(network.Labels).To(HaveLen(1)) - Expect(network.Labels).To(HaveKeyWithValue("mykey", "value")) - }) - - It("dual stack network", func() { - network, err := libpodNet.NetworkInspect("dualstack") - Expect(err).ToNot(HaveOccurred()) - Expect(network.Name).To(Equal("dualstack")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.NetworkInterface).To(Equal("cni-podman21")) - Expect(network.Driver).To(Equal("bridge")) - Expect(network.Subnets).To(HaveLen(2)) - - sub1, _ := types.ParseCIDR("fd10:88:a::/64") - sub2, _ := types.ParseCIDR("10.89.19.0/24") - Expect(network.Subnets).To(ContainElements( - types.Subnet{Subnet: sub1, Gateway: net.ParseIP("fd10:88:a::1")}, - types.Subnet{Subnet: sub2, Gateway: net.ParseIP("10.89.19.10").To4()}, - )) - }) - - It("ipam static network", func() { - network, err := libpodNet.NetworkInspect("ipam-static") - Expect(err).ToNot(HaveOccurred()) - Expect(network.Name).To(Equal("ipam-static")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.Driver).To(Equal("bridge")) - Expect(network.Subnets).To(BeEmpty()) - Expect(network.IPAMOptions).To(HaveKeyWithValue("driver", "static")) - }) - - It("ipam none network", func() { - network, err := libpodNet.NetworkInspect("ipam-none") - Expect(err).ToNot(HaveOccurred()) - Expect(network.Name).To(Equal("ipam-none")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.Driver).To(Equal("bridge")) - Expect(network.Subnets).To(BeEmpty()) - Expect(network.IPAMOptions).To(HaveKeyWithValue("driver", "none")) - }) - - It("ipam empty network", func() { - network, err := libpodNet.NetworkInspect("ipam-empty") - Expect(err).ToNot(HaveOccurred()) - Expect(network.Name).To(Equal("ipam-empty")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.Driver).To(Equal("bridge")) - Expect(network.Subnets).To(BeEmpty()) - Expect(network.IPAMOptions).To(HaveKeyWithValue("driver", "none")) - }) - - It("bridge with isolate option", func() { - network, err := libpodNet.NetworkInspect("isolate") - Expect(err).ToNot(HaveOccurred()) - Expect(network.Name).To(Equal("isolate")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.Driver).To(Equal("bridge")) - Expect(network.Options).To(HaveKeyWithValue("isolate", "true")) - }) - - It("network list with filters (name)", func() { - filters := map[string][]string{ - "name": {"internal", "bridge"}, - } - filterFuncs, err := util.GenerateNetworkFilters(filters) - Expect(err).ToNot(HaveOccurred()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).ToNot(HaveOccurred()) - Expect(networks).To(HaveLen(2)) - Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"))) - }) - - It("network list with filters (partial name)", func() { - filters := map[string][]string{ - "name": {"inte", "bri"}, - } - filterFuncs, err := util.GenerateNetworkFilters(filters) - Expect(err).ToNot(HaveOccurred()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).ToNot(HaveOccurred()) - Expect(networks).To(HaveLen(2)) - Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"))) - }) - - It("network list with filters (id)", func() { - filters := map[string][]string{ - "id": {"3bed2cb3a3acf7b6a8ef408420cc682d5520e26976d354254f528c965612054f", "17f29b073143d8cd97b5bbe492bdeffec1c5fee55cc1fe2112c8b9335f8b6121"}, - } - filterFuncs, err := util.GenerateNetworkFilters(filters) - Expect(err).ToNot(HaveOccurred()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).ToNot(HaveOccurred()) - Expect(networks).To(HaveLen(2)) - Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"))) - }) - - It("network list with filters (id)", func() { - filters := map[string][]string{ - "id": {"3bed2cb3a3acf7b6a8ef408420cc682d5520e26976d354254f528c965612054f", "17f29b073143d8cd97b5bbe492bdeffec1c5fee55cc1fe2112c8b9335f8b6121"}, - } - filterFuncs, err := util.GenerateNetworkFilters(filters) - Expect(err).ToNot(HaveOccurred()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).ToNot(HaveOccurred()) - Expect(networks).To(HaveLen(2)) - Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"))) - }) - - It("network list with filters (partial id)", func() { - filters := map[string][]string{ - "id": {"3bed2cb3a3acf7b6a8ef408420", "17f29b073143d8cd97b5bbe492bde"}, - } - filterFuncs, err := util.GenerateNetworkFilters(filters) - Expect(err).ToNot(HaveOccurred()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).ToNot(HaveOccurred()) - Expect(networks).To(HaveLen(2)) - Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"))) - }) - - It("network list with filters (driver)", func() { - filters := map[string][]string{ - "driver": {"bridge", "macvlan"}, - } - filterFuncs, err := util.GenerateNetworkFilters(filters) - Expect(err).ToNot(HaveOccurred()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).ToNot(HaveOccurred()) - Expect(networks).To(HaveLen(numberOfConfigFiles)) - Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"), - HaveNetworkName("mtu"), HaveNetworkName("vlan"), HaveNetworkName("podman"), - HaveNetworkName("label"), HaveNetworkName("macvlan"), HaveNetworkName("macvlan_mtu"), - HaveNetworkName("dualstack"), HaveNetworkName("ipam-none"), HaveNetworkName("ipam-empty"), - HaveNetworkName("ipam-static"), HaveNetworkName("isolate"))) - }) - - It("network list with filters (label)", func() { - filters := map[string][]string{ - "label": {"mykey"}, - } - filterFuncs, err := util.GenerateNetworkFilters(filters) - Expect(err).ToNot(HaveOccurred()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).ToNot(HaveOccurred()) - Expect(networks).To(HaveLen(1)) - Expect(networks).To(ConsistOf(HaveNetworkName("label"))) - - filters = map[string][]string{ - "label": {"mykey=value"}, - } - filterFuncs, err = util.GenerateNetworkFilters(filters) - Expect(err).ToNot(HaveOccurred()) - - networks, err = libpodNet.NetworkList(filterFuncs...) - Expect(err).ToNot(HaveOccurred()) - Expect(networks).To(HaveLen(1)) - Expect(networks).To(ConsistOf(HaveNetworkName("label"))) - }) - - It("network list with filters", func() { - filters := map[string][]string{ - "driver": {"bridge"}, - "label": {"mykey"}, - } - filterFuncs, err := util.GenerateNetworkFilters(filters) - Expect(err).ToNot(HaveOccurred()) - Expect(filterFuncs).To(HaveLen(2)) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).ToNot(HaveOccurred()) - Expect(networks).To(HaveLen(1)) - Expect(networks).To(ConsistOf(HaveNetworkName("label"))) - - filters = map[string][]string{ - "driver": {"macvlan"}, - "label": {"mykey"}, - } - filterFuncs, err = util.GenerateNetworkFilters(filters) - Expect(err).ToNot(HaveOccurred()) - - networks, err = libpodNet.NetworkList(filterFuncs...) - Expect(err).ToNot(HaveOccurred()) - Expect(networks).To(BeEmpty()) - }) - - It("create bridge network with used interface name", func() { - network := types.Network{ - NetworkInterface: "cni-podman9", - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("bridge name cni-podman9 already in use")) - }) - }) - - Context("network load invalid existing ones", func() { - BeforeEach(func() { - dir := "testfiles/invalid" - files, err := os.ReadDir(dir) - if err != nil { - Fail("Failed to read test directory") - } - for _, file := range files { - filename := file.Name() - data, err := os.ReadFile(filepath.Join(dir, filename)) - if err != nil { - Fail("Failed to copy test files") - } - err = os.WriteFile(filepath.Join(cniConfDir, filename), data, 0o700) - if err != nil { - Fail("Failed to copy test files") - } - } - }) - - It("load invalid networks from disk", func() { - nets, err := libpodNet.NetworkList() - Expect(err).ToNot(HaveOccurred()) - Expect(nets).To(HaveLen(2)) - logString := logBuffer.String() - Expect(logString).To(ContainSubstring("noname.conflist: error parsing configuration list: no name")) - Expect(logString).To(ContainSubstring("noplugin.conflist: error parsing configuration list: no plugins in list")) - Expect(logString).To(ContainSubstring("invalidname.conflist has invalid name, skipping: names must match")) - Expect(logString).To(ContainSubstring("has the same network name as")) - Expect(logString).To(ContainSubstring("broken.conflist: error parsing configuration list")) - Expect(logString).To(ContainSubstring("invalid_gateway.conflist could not be converted to a libpod config, skipping: failed to parse gateway ip 10.89.8")) - }) - }) -}) - -func grepInFile(path, match string) { - grepFile(false, path, match) -} - -func grepNotFile(path, match string) { - grepFile(true, path, match) -} - -func grepFile(not bool, path, match string) { - data, err := os.ReadFile(path) - ExpectWithOffset(2, err).ToNot(HaveOccurred()) - matcher := ContainSubstring(match) - if not { - matcher = Not(matcher) - } - ExpectWithOffset(2, string(data)).To(matcher) -} - -// HaveNetworkName is a custom GomegaMatcher to match a network name. -func HaveNetworkName(name string) gomegaTypes.GomegaMatcher { - return WithTransform(func(e types.Network) string { - return e.Name - }, Equal(name)) -} diff --git a/common/libnetwork/cni/network.go b/common/libnetwork/cni/network.go deleted file mode 100644 index 8a22773388..0000000000 --- a/common/libnetwork/cni/network.go +++ /dev/null @@ -1,358 +0,0 @@ -//go:build (linux || freebsd) && cni - -package cni - -import ( - "context" - "crypto/sha256" - "encoding/hex" - "errors" - "fmt" - "io/fs" - "os" - "path/filepath" - "strings" - "time" - - "github.com/containernetworking/cni/libcni" - "github.com/sirupsen/logrus" - "go.podman.io/common/libnetwork/internal/rootlessnetns" - "go.podman.io/common/libnetwork/types" - "go.podman.io/common/pkg/config" - "go.podman.io/common/pkg/version" - "go.podman.io/storage/pkg/fileutils" - "go.podman.io/storage/pkg/lockfile" - "go.podman.io/storage/pkg/unshare" -) - -const defaultRootLockPath = "/run/lock/podman-cni.lock" - -type cniNetwork struct { - // cniConfigDir is directory where the cni config files are stored. - cniConfigDir string - // cniPluginDirs is a list of directories where cni should look for the plugins. - cniPluginDirs []string - - cniConf *libcni.CNIConfig - - // defaultNetwork is the name for the default network. - defaultNetwork string - // defaultSubnet is the default subnet for the default network. - defaultSubnet types.IPNet - - // defaultsubnetPools contains the subnets which must be used to allocate a free subnet by network create - defaultsubnetPools []config.SubnetPool - - // isMachine describes whenever podman runs in a podman machine environment. - isMachine bool - - // lock is a internal lock for critical operations - lock *lockfile.LockFile - - // modTime is the timestamp when the config dir was modified - modTime time.Time - - // networks is a map with loaded networks, the key is the network name - networks map[string]*network - - // rootlessNetns is used for the rootless network setup/teardown - rootlessNetns *rootlessnetns.Netns -} - -type network struct { - // filename is the full path to the cni config file on disk - filename string - libpodNet *types.Network - cniNet *libcni.NetworkConfigList -} - -type InitConfig struct { - // CNIConfigDir is directory where the cni config files are stored. - CNIConfigDir string - // RunDir is a directory where temporary files can be stored. - RunDir string - - // IsMachine describes whenever podman runs in a podman machine environment. - IsMachine bool - - // Config containers.conf options - Config *config.Config -} - -// NewCNINetworkInterface creates the ContainerNetwork interface for the CNI backend. -// Note: The networks are not loaded from disk until a method is called. -func NewCNINetworkInterface(conf *InitConfig) (types.ContainerNetwork, error) { - var netns *rootlessnetns.Netns - var err error - // Do not use unshare.IsRootless() here. We only care if we are running re-exec in the userns, - // IsRootless() also returns true if we are root in a userns which is not what we care about and - // causes issues as this slower more complicated rootless-netns logic should not be used as root. - val, ok := os.LookupEnv(unshare.UsernsEnvName) - useRootlessNetns := ok && val == "done" - if useRootlessNetns { - netns, err = rootlessnetns.New(conf.RunDir, rootlessnetns.CNI, conf.Config) - if err != nil { - return nil, err - } - } - - // root needs to use a globally unique lock because there is only one host netns - lockPath := defaultRootLockPath - if useRootlessNetns { - lockPath = filepath.Join(conf.CNIConfigDir, "cni.lock") - } - - lock, err := lockfile.GetLockFile(lockPath) - if err != nil { - return nil, err - } - - defaultNetworkName := conf.Config.Network.DefaultNetwork - if defaultNetworkName == "" { - defaultNetworkName = types.DefaultNetworkName - } - - defaultSubnet := conf.Config.Network.DefaultSubnet - if defaultSubnet == "" { - defaultSubnet = types.DefaultSubnet - } - defaultNet, err := types.ParseCIDR(defaultSubnet) - if err != nil { - return nil, fmt.Errorf("failed to parse default subnet: %w", err) - } - - defaultSubnetPools := conf.Config.Network.DefaultSubnetPools - if defaultSubnetPools == nil { - defaultSubnetPools = config.DefaultSubnetPools - } - - cni := libcni.NewCNIConfig(conf.Config.Network.CNIPluginDirs.Values, &cniExec{}) - n := &cniNetwork{ - cniConfigDir: conf.CNIConfigDir, - cniPluginDirs: conf.Config.Network.CNIPluginDirs.Get(), - cniConf: cni, - defaultNetwork: defaultNetworkName, - defaultSubnet: defaultNet, - defaultsubnetPools: defaultSubnetPools, - isMachine: conf.IsMachine, - lock: lock, - rootlessNetns: netns, - } - - return n, nil -} - -// Drivers will return the list of supported network drivers -// for this interface. -func (n *cniNetwork) Drivers() []string { - return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver, types.IPVLANNetworkDriver} -} - -// DefaultNetworkName will return the default cni network name. -func (n *cniNetwork) DefaultNetworkName() string { - return n.defaultNetwork -} - -func (n *cniNetwork) loadNetworks() error { - // check the mod time of the config dir - var modTime time.Time - f, err := os.Stat(n.cniConfigDir) - // ignore error if the file does not exists - if err != nil && !errors.Is(err, os.ErrNotExist) { - return err - } - if err == nil { - modTime = f.ModTime() - } - - // skip loading networks if they are already loaded and - // if the config dir was not modified since the last call - if n.networks != nil && modTime.Equal(n.modTime) { - return nil - } - // make sure the remove all networks before we reload them - n.networks = nil - n.modTime = modTime - - // FIXME: do we have to support other file types as well, e.g. .conf? - files, err := libcni.ConfFiles(n.cniConfigDir, []string{".conflist"}) - if err != nil { - return err - } - networks := make(map[string]*network, len(files)) - for _, file := range files { - conf, err := libcni.ConfListFromFile(file) - if err != nil { - // do not log ENOENT errors - if !errors.Is(err, os.ErrNotExist) { - logrus.Warnf("Error loading CNI config file %s: %v", file, err) - } - continue - } - - if !types.NameRegex.MatchString(conf.Name) { - logrus.Warnf("CNI config list %s has invalid name, skipping: %v", file, types.ErrInvalidName) - continue - } - - // podman < v4.0 used the podman-machine cni plugin for podman machine port forwarding - // since this is now build into podman we no longer use the plugin - // old configs may still contain it so we just remove it here - if n.isMachine { - conf = removeMachinePlugin(conf) - } - - if _, err := n.cniConf.ValidateNetworkList(context.Background(), conf); err != nil { - logrus.Warnf("Error validating CNI config file %s: %v", file, err) - continue - } - - if val, ok := networks[conf.Name]; ok { - logrus.Warnf("CNI config list %s has the same network name as %s, skipping", file, val.filename) - continue - } - - net, err := createNetworkFromCNIConfigList(conf, file) - if err != nil { - // ignore ENOENT as the config has been removed in the meantime so we can just ignore this case - if !errors.Is(err, fs.ErrNotExist) { - logrus.Errorf("CNI config list %s could not be converted to a libpod config, skipping: %v", file, err) - } - continue - } - logrus.Debugf("Successfully loaded network %s: %v", net.Name, net) - networkInfo := network{ - filename: file, - cniNet: conf, - libpodNet: net, - } - networks[net.Name] = &networkInfo - } - - // create the default network in memory if it did not exists on disk - if networks[n.defaultNetwork] == nil { - networkInfo, err := n.createDefaultNetwork() - if err != nil { - return fmt.Errorf("failed to create default network %s: %w", n.defaultNetwork, err) - } - networks[n.defaultNetwork] = networkInfo - } - - logrus.Debugf("Successfully loaded %d networks", len(networks)) - n.networks = networks - return nil -} - -func (n *cniNetwork) createDefaultNetwork() (*network, error) { - net := types.Network{ - Name: n.defaultNetwork, - NetworkInterface: "cni-podman0", - Driver: types.BridgeNetworkDriver, - Subnets: []types.Subnet{ - {Subnet: n.defaultSubnet}, - }, - } - return n.networkCreate(&net, true) -} - -// getNetwork will lookup a network by name or ID. It returns an -// error when no network was found or when more than one network -// with the given (partial) ID exists. -// getNetwork will read from the networks map, therefore the caller -// must ensure that n.lock is locked before using it. -func (n *cniNetwork) getNetwork(nameOrID string) (*network, error) { - // fast path check the map key, this will only work for names - if val, ok := n.networks[nameOrID]; ok { - return val, nil - } - // If there was no match we might got a full or partial ID. - var net *network - for _, val := range n.networks { - // This should not happen because we already looked up the map by name but check anyway. - if val.libpodNet.Name == nameOrID { - return val, nil - } - - if strings.HasPrefix(val.libpodNet.ID, nameOrID) { - if net != nil { - return nil, fmt.Errorf("more than one result for network ID %s", nameOrID) - } - net = val - } - } - if net != nil { - return net, nil - } - return nil, fmt.Errorf("unable to find network with name or ID %s: %w", nameOrID, types.ErrNoSuchNetwork) -} - -// getNetworkIDFromName creates a network ID from the name. It is just the -// sha256 hash so it is not safe but it should be safe enough for our use case. -func getNetworkIDFromName(name string) string { - hash := sha256.Sum256([]byte(name)) - return hex.EncodeToString(hash[:]) -} - -// Implement the NetUtil interface for easy code sharing with other network interfaces. - -// ForEach call the given function for each network. -func (n *cniNetwork) ForEach(run func(types.Network)) { - for _, val := range n.networks { - run(*val.libpodNet) - } -} - -// Len return the number of networks. -func (n *cniNetwork) Len() int { - return len(n.networks) -} - -// DefaultInterfaceName return the default cni bridge name, must be suffixed with a number. -func (n *cniNetwork) DefaultInterfaceName() string { - return cniDeviceName -} - -// NetworkInfo return the network information about binary path, -// package version and program version. -func (n *cniNetwork) NetworkInfo() types.NetworkInfo { - path := "" - packageVersion := "" - for _, p := range n.cniPluginDirs { - ver := version.Package(p) - if ver != version.UnknownPackage { - path = p - packageVersion = ver - break - } - } - - info := types.NetworkInfo{ - Backend: types.CNI, - Package: packageVersion, - Path: path, - } - - dnsPath := filepath.Join(path, "dnsname") - dnsPackage := version.Package(dnsPath) - dnsProgram, err := version.ProgramDnsname(dnsPath) - if err != nil { - logrus.Infof("Failed to get the dnsname plugin version: %v", err) - } - if err := fileutils.Exists(dnsPath); err == nil { - info.DNS = types.DNSNetworkInfo{ - Path: dnsPath, - Package: dnsPackage, - Version: dnsProgram, - } - } - - return info -} - -func (n *cniNetwork) Network(nameOrID string) (*types.Network, error) { - network, err := n.getNetwork(nameOrID) - if err != nil { - return nil, err - } - return network.libpodNet, err -} diff --git a/common/libnetwork/cni/run.go b/common/libnetwork/cni/run.go deleted file mode 100644 index 7877891a9a..0000000000 --- a/common/libnetwork/cni/run.go +++ /dev/null @@ -1,302 +0,0 @@ -//go:build (linux || freebsd) && cni - -package cni - -import ( - "context" - "fmt" - "net" - "os" - "strings" - - "github.com/containernetworking/cni/libcni" - cnitypes "github.com/containernetworking/cni/pkg/types" - types040 "github.com/containernetworking/cni/pkg/types/040" - "github.com/hashicorp/go-multierror" - "github.com/sirupsen/logrus" - "go.podman.io/common/libnetwork/internal/util" - "go.podman.io/common/libnetwork/types" -) - -// Setup will setup the container network namespace. It returns -// a map of StatusBlocks, the key is the network name. -func (n *cniNetwork) Setup(namespacePath string, options types.SetupOptions) (map[string]types.StatusBlock, error) { - n.lock.Lock() - defer n.lock.Unlock() - err := n.loadNetworks() - if err != nil { - return nil, err - } - - err = util.ValidateSetupOptions(n, namespacePath, options) - if err != nil { - return nil, err - } - - err = setupLoopback(namespacePath) - if err != nil { - return nil, fmt.Errorf("failed to set the loopback adapter up: %w", err) - } - - results := make(map[string]types.StatusBlock, len(options.Networks)) - - setup := func() error { - var retErr error - teardownOpts := options - teardownOpts.Networks = map[string]types.PerNetworkOptions{} - // make sure to teardown the already connected networks on error - defer func() { - if retErr != nil { - if len(teardownOpts.Networks) > 0 { - err := n.teardown(namespacePath, types.TeardownOptions(teardownOpts)) - if err != nil { - logrus.Warn(err) - } - } - } - }() - - ports, err := convertSpecgenPortsToCNIPorts(options.PortMappings) - if err != nil { - return err - } - - for name, netOpts := range options.Networks { - network := n.networks[name] - rt := getRuntimeConfig(namespacePath, options.ContainerName, options.ContainerID, name, ports, &netOpts) - - // If we have more than one static ip we need parse the ips via runtime config, - // make sure to add the ips capability to the first plugin otherwise it doesn't get the ips - if len(netOpts.StaticIPs) > 0 && !network.cniNet.Plugins[0].Network.Capabilities["ips"] { - caps := map[string]any{ - "capabilities": map[string]bool{"ips": true}, - } - network.cniNet.Plugins[0], retErr = libcni.InjectConf(network.cniNet.Plugins[0], caps) - if retErr != nil { - return retErr - } - } - - var res cnitypes.Result - res, retErr = n.cniConf.AddNetworkList(context.Background(), network.cniNet, rt) - // Add this network to teardown opts since it is now connected. - // Also add this if an errors was returned since we want to call teardown on this regardless. - teardownOpts.Networks[name] = netOpts - if retErr != nil { - return retErr - } - - logrus.Debugf("cni result for container %s network %s: %v", options.ContainerID, name, res) - var status types.StatusBlock - status, retErr = CNIResultToStatus(res) - if retErr != nil { - return retErr - } - results[name] = status - } - return nil - } - - if n.rootlessNetns != nil { - err = n.rootlessNetns.Setup(len(options.Networks), setup) - } else { - err = setup() - } - return results, err -} - -// CNIResultToStatus convert the cni result to status block -// nolint:golint,revive -func CNIResultToStatus(res cnitypes.Result) (types.StatusBlock, error) { - result := types.StatusBlock{} - cniResult, err := types040.GetResult(res) - if err != nil { - return result, err - } - nameservers := make([]net.IP, 0, len(cniResult.DNS.Nameservers)) - for _, nameserver := range cniResult.DNS.Nameservers { - ip := net.ParseIP(nameserver) - if ip == nil { - return result, fmt.Errorf("failed to parse cni nameserver ip %s", nameserver) - } - nameservers = append(nameservers, ip) - } - result.DNSServerIPs = nameservers - result.DNSSearchDomains = cniResult.DNS.Search - - interfaces := make(map[string]types.NetInterface) - for intIndex, netInterface := range cniResult.Interfaces { - // we are only interested about interfaces in the container namespace - if netInterface.Sandbox == "" { - continue - } - - mac, err := net.ParseMAC(netInterface.Mac) - if err != nil { - return result, err - } - subnets := make([]types.NetAddress, 0, len(cniResult.IPs)) - for _, ip := range cniResult.IPs { - if ip.Interface == nil { - // we do no expect ips without an interface - continue - } - if len(cniResult.Interfaces) <= *ip.Interface { - return result, fmt.Errorf("invalid cni result, interface index %d out of range", *ip.Interface) - } - - // when we have a ip for this interface add it to the subnets - if *ip.Interface == intIndex { - subnets = append(subnets, types.NetAddress{ - IPNet: types.IPNet{IPNet: ip.Address}, - Gateway: ip.Gateway, - }) - } - } - interfaces[netInterface.Name] = types.NetInterface{ - MacAddress: types.HardwareAddr(mac), - Subnets: subnets, - } - } - result.Interfaces = interfaces - return result, nil -} - -func getRuntimeConfig(netns, conName, conID, networkName string, ports []cniPortMapEntry, opts *types.PerNetworkOptions) *libcni.RuntimeConf { - rt := &libcni.RuntimeConf{ - ContainerID: conID, - NetNS: netns, - IfName: opts.InterfaceName, - Args: [][2]string{ - {"IgnoreUnknown", "1"}, - // Do not set the K8S env vars, see https://github.com/containers/podman/issues/12083. - // Only K8S_POD_NAME is used by dnsname to get the container name. - {"K8S_POD_NAME", conName}, - }, - CapabilityArgs: map[string]any{}, - } - - // Propagate environment CNI_ARGS - for kvpairs := range strings.SplitSeq(os.Getenv("CNI_ARGS"), ";") { - if key, val, ok := strings.Cut(kvpairs, "="); ok { - rt.Args = append(rt.Args, [2]string{key, val}) - } - } - - // Add mac address to cni args - if len(opts.StaticMAC) > 0 { - rt.Args = append(rt.Args, [2]string{"MAC", opts.StaticMAC.String()}) - } - - if len(opts.StaticIPs) == 1 { - // Add a single IP to the args field. CNI plugins < 1.0.0 - // do not support multiple ips via capability args. - rt.Args = append(rt.Args, [2]string{"IP", opts.StaticIPs[0].String()}) - } else if len(opts.StaticIPs) > 1 { - // Set the static ips in the capability args - // to support more than one static ip per network. - rt.CapabilityArgs["ips"] = opts.StaticIPs - } - - // Set network aliases for the dnsname plugin. - if len(opts.Aliases) > 0 { - rt.CapabilityArgs["aliases"] = map[string][]string{ - networkName: opts.Aliases, - } - } - - // Set PortMappings in Capabilities - if len(ports) > 0 { - rt.CapabilityArgs["portMappings"] = ports - } - - return rt -} - -// Teardown will teardown the container network namespace. -func (n *cniNetwork) Teardown(namespacePath string, options types.TeardownOptions) error { - n.lock.Lock() - defer n.lock.Unlock() - err := n.loadNetworks() - if err != nil { - return err - } - return n.teardown(namespacePath, options) -} - -func (n *cniNetwork) teardown(namespacePath string, options types.TeardownOptions) error { - // Note: An empty namespacePath is allowed because some plugins - // still need teardown, for example ipam should remove used ip allocations. - - ports, err := convertSpecgenPortsToCNIPorts(options.PortMappings) - if err != nil { - return err - } - - var multiErr *multierror.Error - teardown := func() error { - for name, netOpts := range options.Networks { - rt := getRuntimeConfig(namespacePath, options.ContainerName, options.ContainerID, name, ports, &netOpts) - - cniConfList, newRt, err := getCachedNetworkConfig(n.cniConf, name, rt) - if err == nil { - rt = newRt - } else { - logrus.Warnf("Failed to load cached network config: %v, falling back to loading network %s from disk", err, name) - network := n.networks[name] - if network == nil { - multiErr = multierror.Append(multiErr, fmt.Errorf("network %s: %w", name, types.ErrNoSuchNetwork)) - continue - } - cniConfList = network.cniNet - } - - err = n.cniConf.DelNetworkList(context.Background(), cniConfList, rt) - if err != nil { - multiErr = multierror.Append(multiErr, err) - } - } - return nil - } - - if n.rootlessNetns != nil { - err = n.rootlessNetns.Teardown(len(options.Networks), teardown) - } else { - err = teardown() - } - multiErr = multierror.Append(multiErr, err) - - return multiErr.ErrorOrNil() -} - -func getCachedNetworkConfig(cniConf *libcni.CNIConfig, name string, rt *libcni.RuntimeConf) (*libcni.NetworkConfigList, *libcni.RuntimeConf, error) { - cniConfList := &libcni.NetworkConfigList{ - Name: name, - } - confBytes, rt, err := cniConf.GetNetworkListCachedConfig(cniConfList, rt) - if err != nil { - return nil, nil, err - } else if confBytes == nil { - return nil, nil, fmt.Errorf("network %s not found in CNI cache", name) - } - - cniConfList, err = libcni.ConfListFromBytes(confBytes) - if err != nil { - return nil, nil, err - } - return cniConfList, rt, nil -} - -func (n *cniNetwork) RunInRootlessNetns(toRun func() error) error { - if n.rootlessNetns == nil { - return types.ErrNotRootlessNetns - } - return n.rootlessNetns.Run(n.lock, toRun) -} - -func (n *cniNetwork) RootlessNetnsInfo() (*types.RootlessNetnsInfo, error) { - if n.rootlessNetns == nil { - return nil, types.ErrNotRootlessNetns - } - return n.rootlessNetns.Info(), nil -} diff --git a/common/libnetwork/cni/run_freebsd.go b/common/libnetwork/cni/run_freebsd.go deleted file mode 100644 index cca00aa83f..0000000000 --- a/common/libnetwork/cni/run_freebsd.go +++ /dev/null @@ -1,19 +0,0 @@ -package cni - -import ( - "os/exec" -) - -// FreeBSD vnet adds the lo0 interface automatically - we just need to -// add the default address. Note: this will also add ::1 as a side -// effect. -func setupLoopback(namespacePath string) error { - // Try to run the command using ifconfig's -j flag (supported in 13.3 and later) - if err := exec.Command("ifconfig", "-j", namespacePath, "lo0", "inet", "127.0.0.1").Run(); err == nil { - return nil - } - - // Fall back to using the jexec wrapper to run the ifconfig command - // inside the jail. - return exec.Command("jexec", namespacePath, "ifconfig", "lo0", "inet", "127.0.0.1").Run() -} diff --git a/common/libnetwork/cni/run_linux.go b/common/libnetwork/cni/run_linux.go deleted file mode 100644 index 735e4960e7..0000000000 --- a/common/libnetwork/cni/run_linux.go +++ /dev/null @@ -1,17 +0,0 @@ -package cni - -import ( - "github.com/containernetworking/plugins/pkg/ns" - "github.com/vishvananda/netlink" -) - -func setupLoopback(namespacePath string) error { - // set the loopback adapter up in the container netns - return ns.WithNetNSPath(namespacePath, func(_ ns.NetNS) error { - link, err := netlink.LinkByName("lo") - if err == nil { - err = netlink.LinkSetUp(link) - } - return err - }) -} diff --git a/common/libnetwork/cni/run_test.go b/common/libnetwork/cni/run_test.go deleted file mode 100644 index 5a266b9937..0000000000 --- a/common/libnetwork/cni/run_test.go +++ /dev/null @@ -1,1412 +0,0 @@ -//go:build (linux || freebsd) && cni - -package cni_test - -// The tests have to be run as root. -// For each test there will be two network namespaces created, -// netNSTest and netNSContainer. Each test must be run inside -// netNSTest to prevent leakage in the host netns, therefore -// it should use the following structure: -// It("test name", func() { -// runTest(func() { -// // add test logic here -// }) -// }) - -import ( - "bytes" - "io" - "net" - "os" - "path/filepath" - "strconv" - "sync" - "time" - - "github.com/containernetworking/plugins/pkg/ns" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" - "go.podman.io/common/libnetwork/types" - "go.podman.io/common/pkg/netns" - "go.podman.io/storage/pkg/stringid" - "go.podman.io/storage/pkg/unshare" - "golang.org/x/sys/unix" -) - -var _ = Describe("run CNI", func() { - var ( - libpodNet types.ContainerNetwork - cniConfDir string - logBuffer bytes.Buffer - netNSTest ns.NetNS - netNSContainer ns.NetNS - ) - const cniVarDir = "/var/lib/cni" - - // runTest is a helper function to run a test. It ensures that each test - // is run in its own netns. It also creates a mounts to mount a tmpfs to /var/lib/cni. - runTest := func(run func()) { - _ = netNSTest.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - err := os.MkdirAll(cniVarDir, 0o755) - Expect(err).ToNot(HaveOccurred(), "Failed to create cniVarDir") - err = unix.Unshare(unix.CLONE_NEWNS) - Expect(err).ToNot(HaveOccurred(), "Failed to create new mounts") - err = unix.Mount("tmpfs", cniVarDir, "tmpfs", unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV, "") - Expect(err).ToNot(HaveOccurred(), "Failed to mount tmpfs for cniVarDir") - defer unix.Unmount(cniVarDir, 0) //nolint:errcheck - - // we have to setup the loopback adapter in this netns to use port forwarding - link, err := netlink.LinkByName("lo") - Expect(err).ToNot(HaveOccurred(), "Failed to get loopback adapter") - err = netlink.LinkSetUp(link) - Expect(err).ToNot(HaveOccurred(), "Failed to set loopback adapter up") - run() - return nil - }) - } - - BeforeEach(func() { - // The tests need root privileges. - // Technically we could work around that by using user namespaces and - // the rootless cni code but this is to much work to get it right for a unit test. - if unshare.IsRootless() { - Skip("this test needs to be run as root") - } - - t := GinkgoT() - cniConfDir = t.TempDir() - logBuffer = bytes.Buffer{} - logrus.SetOutput(&logBuffer) - - var err error - netNSTest, err = netns.NewNS() - if err != nil { - Fail("Failed to create netns") - } - DeferCleanup(func() { - _ = netns.UnmountNS(netNSTest.Path()) - _ = netNSTest.Close() - }) - - netNSContainer, err = netns.NewNS() - if err != nil { - Fail("Failed to create netns") - } - DeferCleanup(func() { - _ = netns.UnmountNS(netNSContainer.Path()) - _ = netNSContainer.Close() - }) - - logrus.SetLevel(logrus.WarnLevel) - DeferCleanup(func() { - logrus.SetLevel(logrus.InfoLevel) - }) - }) - - JustBeforeEach(func() { - var err error - libpodNet, err = getNetworkInterface(cniConfDir) - if err != nil { - Fail("Failed to create NewCNINetworkInterface") - } - }) - - Context("network setup test", func() { - It("run with default config", func() { - runTest(func() { - defNet := types.DefaultNetworkName - intName := "eth0" - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - defNet: {InterfaceName: intName}, - }, - }, - } - res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).ToNot(HaveOccurred()) - Expect(res).To(HaveLen(1)) - Expect(res).To(HaveKey(defNet)) - Expect(res[defNet].Interfaces).To(HaveKey(intName)) - Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1)) - Expect(res[defNet].Interfaces[intName].Subnets[0].IPNet.IP.String()).To(ContainSubstring("10.88.0.")) - Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) - // default network has no dns - Expect(res[defNet].DNSServerIPs).To(BeEmpty()) - Expect(res[defNet].DNSSearchDomains).To(BeEmpty()) - - // reload the interface so the networks are reload from disk - libpodNet, err := getNetworkInterface(cniConfDir) - Expect(err).ToNot(HaveOccurred()) - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).ToNot(HaveOccurred()) - }) - }) - - It("run with default config and static ip", func() { - runTest(func() { - defNet := types.DefaultNetworkName - intName := "eth0" - ip := net.ParseIP("10.88.5.5") - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - defNet: { - InterfaceName: intName, - StaticIPs: []net.IP{ip}, - }, - }, - }, - } - res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).ToNot(HaveOccurred()) - Expect(res).To(HaveLen(1)) - Expect(res).To(HaveKey(defNet)) - Expect(res[defNet].Interfaces).To(HaveKey(intName)) - Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1)) - Expect(res[defNet].Interfaces[intName].Subnets[0].IPNet.IP).To(Equal(ip)) - Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) - // default network has no dns - Expect(res[defNet].DNSServerIPs).To(BeEmpty()) - Expect(res[defNet].DNSSearchDomains).To(BeEmpty()) - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).ToNot(HaveOccurred()) - }) - }) - - for _, proto := range []string{"tcp", "udp"} { - // copy proto to extra var to keep correct references in the goroutines - protocol := proto - It("run with exposed ports protocol "+protocol, func() { - runTest(func() { - testdata := stringid.GenerateNonCryptoID() - defNet := types.DefaultNetworkName - intName := "eth0" - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - PortMappings: []types.PortMapping{{ - Protocol: protocol, - HostIP: "127.0.0.1", - HostPort: 5000, - ContainerPort: 5000, - }}, - Networks: map[string]types.PerNetworkOptions{ - defNet: {InterfaceName: intName}, - }, - }, - } - res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).ToNot(HaveOccurred()) - Expect(res).To(HaveLen(1)) - Expect(res).To(HaveKey(defNet)) - Expect(res[defNet].Interfaces).To(HaveKey(intName)) - Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1)) - Expect(res[defNet].Interfaces[intName].Subnets[0].IPNet.IP.String()).To(ContainSubstring("10.88.0.")) - Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) - // default network has no dns - Expect(res[defNet].DNSServerIPs).To(BeEmpty()) - Expect(res[defNet].DNSSearchDomains).To(BeEmpty()) - var wg sync.WaitGroup - wg.Add(1) - // start a listener in the container ns - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - runNetListener(&wg, protocol, "0.0.0.0", 5000, testdata) - return nil - }) - Expect(err).ToNot(HaveOccurred()) - - conn, err := net.Dial(protocol, "127.0.0.1:5000") - Expect(err).ToNot(HaveOccurred()) - _, err = conn.Write([]byte(testdata)) - Expect(err).ToNot(HaveOccurred()) - conn.Close() - - // wait for the listener to finish - wg.Wait() - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).ToNot(HaveOccurred()) - }) - }) - - It("run with range ports protocol "+protocol, func() { - runTest(func() { - defNet := types.DefaultNetworkName - intName := "eth0" - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - PortMappings: []types.PortMapping{{ - Protocol: protocol, - HostIP: "127.0.0.1", - HostPort: 5001, - ContainerPort: 5000, - Range: 3, - }}, - Networks: map[string]types.PerNetworkOptions{ - defNet: {InterfaceName: intName}, - }, - }, - } - res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).ToNot(HaveOccurred()) - Expect(res).To(HaveLen(1)) - Expect(res).To(HaveKey(defNet)) - Expect(res[defNet].Interfaces).To(HaveKey(intName)) - Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1)) - containerIP := res[defNet].Interfaces[intName].Subnets[0].IPNet.IP.String() - Expect(containerIP).To(ContainSubstring("10.88.0.")) - Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) - // default network has no dns - Expect(res[defNet].DNSServerIPs).To(BeEmpty()) - Expect(res[defNet].DNSSearchDomains).To(BeEmpty()) - - // loop over all ports - for p := 5001; p < 5004; p++ { - port := p - var wg sync.WaitGroup - wg.Add(1) - testdata := stringid.GenerateNonCryptoID() - // start a listener in the container ns - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - runNetListener(&wg, protocol, containerIP, port-1, testdata) - return nil - }) - Expect(err).ToNot(HaveOccurred()) - - conn, err := net.Dial(protocol, net.JoinHostPort("127.0.0.1", strconv.Itoa(port))) - Expect(err).ToNot(HaveOccurred()) - _, err = conn.Write([]byte(testdata)) - Expect(err).ToNot(HaveOccurred()) - conn.Close() - - // wait for the listener to finish - wg.Wait() - } - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).ToNot(HaveOccurred()) - }) - }) - } - - It("run with comma separated port protocol", func() { - runTest(func() { - defNet := types.DefaultNetworkName - intName := "eth0" - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - PortMappings: []types.PortMapping{{ - Protocol: "tcp,udp", - HostIP: "127.0.0.1", - HostPort: 5000, - ContainerPort: 5000, - }}, - Networks: map[string]types.PerNetworkOptions{ - defNet: {InterfaceName: intName}, - }, - }, - } - res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).ToNot(HaveOccurred()) - Expect(res).To(HaveLen(1)) - Expect(res).To(HaveKey(defNet)) - Expect(res[defNet].Interfaces).To(HaveKey(intName)) - Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1)) - Expect(res[defNet].Interfaces[intName].Subnets[0].IPNet.IP.String()).To(ContainSubstring("10.88.0.")) - Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) - - for _, proto := range []string{"tcp", "udp"} { - // copy proto to extra var to keep correct references in the goroutines - protocol := proto - - testdata := stringid.GenerateNonCryptoID() - var wg sync.WaitGroup - wg.Add(1) - // start tcp listener in the container ns - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - runNetListener(&wg, protocol, "0.0.0.0", 5000, testdata) - return nil - }) - Expect(err).ToNot(HaveOccurred()) - - conn, err := net.Dial(protocol, "127.0.0.1:5000") - Expect(err).ToNot(HaveOccurred()) - _, err = conn.Write([]byte(testdata)) - Expect(err).ToNot(HaveOccurred()) - conn.Close() - - // wait for the listener to finish - wg.Wait() - } - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).ToNot(HaveOccurred()) - }) - }) - - It("call setup twice", func() { - runTest(func() { - network := types.Network{} - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - - intName1 := "eth0" - netName1 := network1.Name - - containerID := stringid.GenerateNonCryptoID() - - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: containerID, - Networks: map[string]types.PerNetworkOptions{ - netName1: { - InterfaceName: intName1, - }, - }, - }, - } - - res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).ToNot(HaveOccurred()) - Expect(res).To(HaveLen(1)) - - Expect(res).To(HaveKey(netName1)) - Expect(res[netName1].Interfaces).To(HaveKey(intName1)) - Expect(res[netName1].Interfaces[intName1].Subnets).To(HaveLen(1)) - ipInt1 := res[netName1].Interfaces[intName1].Subnets[0].IPNet.IP - Expect(ipInt1).ToNot(BeEmpty()) - macInt1 := res[netName1].Interfaces[intName1].MacAddress - Expect(macInt1).To(HaveLen(6)) - - // check in the container namespace if the settings are applied - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - i, err := net.InterfaceByName(intName1) - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal(intName1)) - Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt1))) - addrs, err := i.Addrs() - Expect(err).ToNot(HaveOccurred()) - subnet := &net.IPNet{ - IP: ipInt1, - Mask: net.CIDRMask(24, 32), - } - Expect(addrs).To(ContainElements(subnet)) - - // check loopback adapter - i, err = net.InterfaceByName("lo") - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal("lo")) - Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) - Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") - return nil - }) - Expect(err).ToNot(HaveOccurred()) - - network = types.Network{} - network2, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - - intName2 := "eth1" - netName2 := network2.Name - - setupOpts.Networks = map[string]types.PerNetworkOptions{ - netName2: { - InterfaceName: intName2, - }, - } - - res, err = libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).ToNot(HaveOccurred()) - Expect(res).To(HaveLen(1)) - - Expect(res).To(HaveKey(netName2)) - Expect(res[netName2].Interfaces).To(HaveKey(intName2)) - Expect(res[netName2].Interfaces[intName2].Subnets).To(HaveLen(1)) - ipInt2 := res[netName2].Interfaces[intName2].Subnets[0].IPNet.IP - Expect(ipInt2).ToNot(BeEmpty()) - macInt2 := res[netName2].Interfaces[intName2].MacAddress - Expect(macInt2).To(HaveLen(6)) - - // check in the container namespace if the settings are applied - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - i, err := net.InterfaceByName(intName1) - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal(intName1)) - Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt1))) - addrs, err := i.Addrs() - Expect(err).ToNot(HaveOccurred()) - subnet := &net.IPNet{ - IP: ipInt1, - Mask: net.CIDRMask(24, 32), - } - Expect(addrs).To(ContainElements(subnet)) - - i, err = net.InterfaceByName(intName2) - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal(intName2)) - Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt2))) - addrs, err = i.Addrs() - Expect(err).ToNot(HaveOccurred()) - subnet = &net.IPNet{ - IP: ipInt2, - Mask: net.CIDRMask(24, 32), - } - Expect(addrs).To(ContainElements(subnet)) - - // check loopback adapter - i, err = net.InterfaceByName("lo") - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal("lo")) - Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) - Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") - return nil - }) - Expect(err).ToNot(HaveOccurred()) - - teatdownOpts := types.TeardownOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: containerID, - Networks: map[string]types.PerNetworkOptions{ - netName1: { - InterfaceName: intName1, - }, - netName2: { - InterfaceName: intName2, - }, - }, - }, - } - - err = libpodNet.Teardown(netNSContainer.Path(), teatdownOpts) - Expect(err).ToNot(HaveOccurred()) - logString := logBuffer.String() - Expect(logString).To(BeEmpty()) - - // check in the container namespace that the interface is removed - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - _, err := net.InterfaceByName(intName1) - Expect(err).To(HaveOccurred()) - _, err = net.InterfaceByName(intName2) - Expect(err).To(HaveOccurred()) - - // check that only the loopback adapter is left - ints, err := net.Interfaces() - Expect(err).ToNot(HaveOccurred()) - Expect(ints).To(HaveLen(1)) - Expect(ints[0].Name).To(Equal("lo")) - Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) - Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") - - return nil - }) - Expect(err).ToNot(HaveOccurred()) - - err = libpodNet.NetworkRemove(netName1) - Expect(err).ToNot(HaveOccurred()) - err = libpodNet.NetworkRemove(netName2) - Expect(err).ToNot(HaveOccurred()) - - // check that the interfaces are removed in the host ns - _, err = net.InterfaceByName(network1.NetworkInterface) - Expect(err).To(HaveOccurred()) - _, err = net.InterfaceByName(network2.NetworkInterface) - Expect(err).To(HaveOccurred()) - }) - }) - - It("setup two networks with one setup call", func() { - runTest(func() { - subnet1, _ := types.ParseCIDR("192.168.0.0/24") - subnet2, _ := types.ParseCIDR("192.168.1.0/24") - network := types.Network{ - Subnets: []types.Subnet{ - {Subnet: subnet1}, - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - - network = types.Network{ - Subnets: []types.Subnet{ - {Subnet: subnet2}, - }, - } - network2, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - - intName1 := "eth0" - intName2 := "eth1" - netName1 := network1.Name - netName2 := network2.Name - - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - netName1: { - InterfaceName: intName1, - }, - netName2: { - InterfaceName: intName2, - }, - }, - }, - } - - res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).ToNot(HaveOccurred()) - Expect(res).To(HaveLen(2)) - - Expect(res).To(HaveKey(netName1)) - Expect(res[netName1].Interfaces).To(HaveKey(intName1)) - Expect(res[netName1].Interfaces[intName1].Subnets).To(HaveLen(1)) - ipInt1 := res[netName1].Interfaces[intName1].Subnets[0].IPNet.IP - Expect(ipInt1.String()).To(ContainSubstring("192.168.0.")) - macInt1 := res[netName1].Interfaces[intName1].MacAddress - Expect(macInt1).To(HaveLen(6)) - - Expect(res).To(HaveKey(netName2)) - Expect(res[netName2].Interfaces).To(HaveKey(intName2)) - Expect(res[netName2].Interfaces[intName2].Subnets).To(HaveLen(1)) - ipInt2 := res[netName2].Interfaces[intName2].Subnets[0].IPNet.IP - Expect(ipInt2.String()).To(ContainSubstring("192.168.1.")) - macInt2 := res[netName2].Interfaces[intName2].MacAddress - Expect(macInt2).To(HaveLen(6)) - - // default network has no dns - Expect(res[netName1].DNSServerIPs).To(BeEmpty()) - Expect(res[netName1].DNSSearchDomains).To(BeEmpty()) - - // check in the container namespace if the settings are applied - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - i, err := net.InterfaceByName(intName1) - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal(intName1)) - Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt1))) - addrs, err := i.Addrs() - Expect(err).ToNot(HaveOccurred()) - subnet := &net.IPNet{ - IP: ipInt1, - Mask: net.CIDRMask(24, 32), - } - Expect(addrs).To(ContainElements(subnet)) - - i, err = net.InterfaceByName(intName2) - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal(intName2)) - Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt2))) - addrs, err = i.Addrs() - Expect(err).ToNot(HaveOccurred()) - subnet = &net.IPNet{ - IP: ipInt2, - Mask: net.CIDRMask(24, 32), - } - Expect(addrs).To(ContainElements(subnet)) - - // check loopback adapter - i, err = net.InterfaceByName("lo") - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal("lo")) - Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) - Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") - return nil - }) - Expect(err).ToNot(HaveOccurred()) - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).ToNot(HaveOccurred()) - logString := logBuffer.String() - Expect(logString).To(BeEmpty()) - - // check in the container namespace that the interface is removed - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - _, err := net.InterfaceByName(intName1) - Expect(err).To(HaveOccurred()) - _, err = net.InterfaceByName(intName2) - Expect(err).To(HaveOccurred()) - - // check that only the loopback adapter is left - ints, err := net.Interfaces() - Expect(err).ToNot(HaveOccurred()) - Expect(ints).To(HaveLen(1)) - Expect(ints[0].Name).To(Equal("lo")) - Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) - Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") - - return nil - }) - Expect(err).ToNot(HaveOccurred()) - }) - }) - - It("dual stack network with static ips", func() { - // Version checks for cni plugins are not possible, the plugins do not output - // version information and using the package manager does not work across distros. - // Fedora has the right version so we use this for now. - // requires cni plugins 1.0.0 or newer for multiple static ips - runTest(func() { - subnet1, _ := types.ParseCIDR("192.168.0.0/24") - subnet2, _ := types.ParseCIDR("fd41:0a75:2ca0:48a9::/64") - network := types.Network{ - Subnets: []types.Subnet{ - {Subnet: subnet1}, {Subnet: subnet2}, - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - - mac, _ := net.ParseMAC("40:15:2f:d8:42:36") - interfaceName := "eth0" - - ip1 := net.ParseIP("192.168.0.5") - ip2 := net.ParseIP("fd41:0a75:2ca0:48a9::5") - - netName := network1.Name - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerName: "mycon", - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - netName: { - InterfaceName: interfaceName, - StaticIPs: []net.IP{ip1, ip2}, - StaticMAC: types.HardwareAddr(mac), - }, - }, - }, - } - - res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).ToNot(HaveOccurred()) - Expect(res).To(HaveLen(1)) - Expect(res).To(HaveKey(netName)) - Expect(res[netName].Interfaces).To(HaveKey(interfaceName)) - Expect(res[netName].Interfaces[interfaceName].Subnets).To(HaveLen(2)) - Expect(res[netName].Interfaces[interfaceName].Subnets[0].IPNet.IP.String()).To(Equal(ip1.String())) - Expect(res[netName].Interfaces[interfaceName].Subnets[0].IPNet.Mask).To(Equal(subnet1.Mask)) - Expect(res[netName].Interfaces[interfaceName].Subnets[0].Gateway).To(Equal(net.ParseIP("192.168.0.1"))) - Expect(res[netName].Interfaces[interfaceName].Subnets[1].IPNet.IP.String()).To(Equal(ip2.String())) - Expect(res[netName].Interfaces[interfaceName].Subnets[1].IPNet.Mask).To(Equal(subnet2.Mask)) - Expect(res[netName].Interfaces[interfaceName].Subnets[1].Gateway).To(Equal(net.ParseIP("fd41:0a75:2ca0:48a9::1"))) - Expect(res[netName].Interfaces[interfaceName].MacAddress).To(Equal(types.HardwareAddr(mac))) - // default network has no dns - Expect(res[netName].DNSServerIPs).To(BeEmpty()) - Expect(res[netName].DNSSearchDomains).To(BeEmpty()) - - // check in the container namespace if the settings are applied - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - i, err := net.InterfaceByName(interfaceName) - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal(interfaceName)) - Expect(i.HardwareAddr).To(Equal(mac)) - addrs, err := i.Addrs() - Expect(err).ToNot(HaveOccurred()) - subnet1 := &net.IPNet{ - IP: ip1, - Mask: net.CIDRMask(24, 32), - } - subnet2 := &net.IPNet{ - IP: ip2, - Mask: net.CIDRMask(64, 128), - } - Expect(addrs).To(ContainElements(subnet1, subnet2)) - - // check loopback adapter - i, err = net.InterfaceByName("lo") - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal("lo")) - Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) - Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") - return nil - }) - Expect(err).ToNot(HaveOccurred()) - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).ToNot(HaveOccurred()) - logString := logBuffer.String() - Expect(logString).To(BeEmpty()) - - // check in the container namespace that the interface is removed - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - _, err := net.InterfaceByName(interfaceName) - Expect(err).To(HaveOccurred()) - - // check that only the loopback adapter is left - ints, err := net.Interfaces() - Expect(err).ToNot(HaveOccurred()) - Expect(ints).To(HaveLen(1)) - Expect(ints[0].Name).To(Equal("lo")) - Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) - Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") - - return nil - }) - Expect(err).ToNot(HaveOccurred()) - }) - }) - - It("CNI_ARGS from environment variable", func() { - runTest(func() { - subnet1, _ := types.ParseCIDR("172.16.1.0/24") - ip := "172.16.1.5" - network := types.Network{ - Subnets: []types.Subnet{ - {Subnet: subnet1}, - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - netName := network1.Name - intName := "eth0" - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - netName: { - InterfaceName: intName, - }, - }, - }, - } - - t := GinkgoT() - t.Setenv("CNI_ARGS", "IP="+ip) - - res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).ToNot(HaveOccurred()) - Expect(res).To(HaveLen(1)) - Expect(res).To(HaveKey(netName)) - Expect(res[netName].Interfaces).To(HaveKey(intName)) - Expect(res[netName].Interfaces[intName].Subnets).To(HaveLen(1)) - Expect(res[netName].Interfaces[intName].Subnets[0].IPNet.IP.String()).To(Equal(ip)) - Expect(res[netName].Interfaces[intName].Subnets[0].IPNet.Mask).To(Equal(net.CIDRMask(24, 32))) - - // check in the container namespace if the settings are applied - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - i, err := net.InterfaceByName(intName) - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal(intName)) - addrs, err := i.Addrs() - Expect(err).ToNot(HaveOccurred()) - subnet := &net.IPNet{ - IP: net.ParseIP(ip), - Mask: net.CIDRMask(24, 32), - } - Expect(addrs).To(ContainElements(subnet)) - - // check loopback adapter - i, err = net.InterfaceByName("lo") - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal("lo")) - Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) - Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") - return nil - }) - Expect(err).ToNot(HaveOccurred()) - }) - }) - - It("setup ipam driver none network", func() { - runTest(func() { - network := types.Network{ - IPAMOptions: map[string]string{ - types.Driver: types.NoneIPAMDriver, - }, - DNSEnabled: true, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - - intName1 := "eth0" - netName1 := network1.Name - - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - netName1: { - InterfaceName: intName1, - }, - }, - }, - } - - res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).ToNot(HaveOccurred()) - Expect(res).To(HaveLen(1)) - - Expect(res).To(HaveKey(netName1)) - Expect(res[netName1].Interfaces).To(HaveKey(intName1)) - Expect(res[netName1].Interfaces[intName1].Subnets).To(BeEmpty()) - macInt1 := res[netName1].Interfaces[intName1].MacAddress - Expect(macInt1).To(HaveLen(6)) - - // check in the container namespace if the settings are applied - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - i, err := net.InterfaceByName(intName1) - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal(intName1)) - Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt1))) - addrs, err := i.Addrs() - Expect(err).ToNot(HaveOccurred()) - // we still have the ipv6 link local address - Expect(addrs).To(HaveLen(1)) - addr, ok := addrs[0].(*net.IPNet) - Expect(ok).To(BeTrue(), "cast address to ipnet") - // make sure we are link local - Expect(addr.IP.IsLinkLocalUnicast()).To(BeTrue(), "ip is link local address") - - // check loopback adapter - i, err = net.InterfaceByName("lo") - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal("lo")) - Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) - Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") - return nil - }) - Expect(err).ToNot(HaveOccurred()) - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).ToNot(HaveOccurred()) - logString := logBuffer.String() - Expect(logString).To(BeEmpty()) - - // check in the container namespace that the interface is removed - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - _, err := net.InterfaceByName(intName1) - Expect(err).To(HaveOccurred()) - - // check that only the loopback adapter is left - ints, err := net.Interfaces() - Expect(err).ToNot(HaveOccurred()) - Expect(ints).To(HaveLen(1)) - Expect(ints[0].Name).To(Equal("lo")) - Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) - Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") - - return nil - }) - Expect(err).ToNot(HaveOccurred()) - }) - }) - }) - - Context("network setup test with networks from disk", func() { - BeforeEach(func() { - dir := "testfiles/valid" - files, err := os.ReadDir(dir) - if err != nil { - Fail("Failed to read test directory") - } - for _, file := range files { - filename := file.Name() - data, err := os.ReadFile(filepath.Join(dir, filename)) - if err != nil { - Fail("Failed to copy test files") - } - err = os.WriteFile(filepath.Join(cniConfDir, filename), data, 0o700) - if err != nil { - Fail("Failed to copy test files") - } - } - }) - - It("dualstack setup with static ip and dns", func() { - SkipIfNoDnsname() - // Version checks for cni plugins are not possible, the plugins do not output - // version information and using the package manager does not work across distros. - // Fedora has the right version so we use this for now. - // requires cni plugins 1.0.0 or newer for multiple static ips - runTest(func() { - interfaceName := "eth0" - - ip1 := net.ParseIP("fd10:88:a::11") - ip2 := net.ParseIP("10.89.19.15") - - containerName := "myname" - aliases := []string{"aliasname"} - - netName := "dualstack" - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - ContainerName: containerName, - Networks: map[string]types.PerNetworkOptions{ - netName: { - InterfaceName: interfaceName, - StaticIPs: []net.IP{ip1, ip2}, - Aliases: aliases, - }, - }, - }, - } - - network, err := libpodNet.NetworkInspect(netName) - Expect(err).ToNot(HaveOccurred()) - Expect(network.Name).To(Equal(netName)) - Expect(network.DNSEnabled).To(BeTrue()) - Expect(network.Subnets).To(HaveLen(2)) - gw1 := network.Subnets[0].Gateway - Expect(gw1).To(HaveLen(16)) - mask1 := network.Subnets[0].Subnet.Mask - Expect(mask1).To(HaveLen(16)) - gw2 := network.Subnets[1].Gateway - Expect(gw2).To(HaveLen(4)) - mask2 := network.Subnets[1].Subnet.Mask - Expect(mask2).To(HaveLen(4)) - - // because this net has dns we should always teardown otherwise we leak a dnsmasq process - defer libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) //nolint:errcheck - res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).ToNot(HaveOccurred()) - Expect(res).To(HaveLen(1)) - Expect(res).To(HaveKey(netName)) - Expect(res[netName].Interfaces).To(HaveKey(interfaceName)) - Expect(res[netName].Interfaces[interfaceName].Subnets).To(HaveLen(2)) - Expect(res[netName].Interfaces[interfaceName].Subnets[0].IPNet.IP.String()).To(Equal(ip1.String())) - Expect(res[netName].Interfaces[interfaceName].Subnets[0].IPNet.Mask).To(Equal(mask1)) - Expect(res[netName].Interfaces[interfaceName].Subnets[1].IPNet.IP.String()).To(Equal(ip2.String())) - Expect(res[netName].Interfaces[interfaceName].Subnets[1].IPNet.Mask).To(Equal(mask2)) - // dualstack network dns - Expect(res[netName].DNSServerIPs).To(HaveLen(2)) - Expect(res[netName].DNSSearchDomains).To(HaveLen(1)) - Expect(res[netName].DNSSearchDomains).To(ConsistOf("dns.podman")) - - // check in the container namespace if the settings are applied - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - i, err := net.InterfaceByName(interfaceName) - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal(interfaceName)) - addrs, err := i.Addrs() - Expect(err).ToNot(HaveOccurred()) - subnet1 := &net.IPNet{ - IP: ip1, - Mask: net.CIDRMask(64, 128), - } - subnet2 := &net.IPNet{ - IP: ip2, - Mask: net.CIDRMask(24, 32), - } - Expect(addrs).To(ContainElements(subnet1, subnet2)) - - // check loopback adapter - i, err = net.InterfaceByName("lo") - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal("lo")) - Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) - Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") - - return nil - }) - Expect(err).ToNot(HaveOccurred()) - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).ToNot(HaveOccurred()) - logString := logBuffer.String() - Expect(logString).To(BeEmpty()) - - // check in the container namespace that the interface is removed - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - _, err := net.InterfaceByName(interfaceName) - Expect(err).To(HaveOccurred()) - - // check that only the loopback adapter is left - ints, err := net.Interfaces() - Expect(err).ToNot(HaveOccurred()) - Expect(ints).To(HaveLen(1)) - Expect(ints[0].Name).To(Equal("lo")) - Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) - Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") - - return nil - }) - Expect(err).ToNot(HaveOccurred()) - }) - }) - - It("setup with aliases but dns disabled should work", func() { - runTest(func() { - defNet := types.DefaultNetworkName - intName := "eth0" - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - defNet: { - InterfaceName: intName, - Aliases: []string{"somealias"}, - }, - }, - }, - } - _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).ToNot(HaveOccurred()) - }) - }) - }) - - Context("invalid network setup test", func() { - It("static ip not in subnet", func() { - runTest(func() { - defNet := types.DefaultNetworkName - intName := "eth0" - ip := "1.1.1.1" - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - defNet: { - InterfaceName: intName, - StaticIPs: []net.IP{net.ParseIP(ip)}, - }, - }, - }, - } - _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("requested static ip %s not in any subnet on network %s", ip, defNet)) - }) - }) - - It("setup without namespace path", func() { - runTest(func() { - defNet := types.DefaultNetworkName - intName := "eth0" - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - defNet: { - InterfaceName: intName, - }, - }, - }, - } - _, err := libpodNet.Setup("", setupOpts) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("namespacePath is empty")) - }) - }) - - It("setup with invalid namespace path", func() { - runTest(func() { - defNet := types.DefaultNetworkName - intName := "eth0" - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - defNet: { - InterfaceName: intName, - }, - }, - }, - } - _, err := libpodNet.Setup("some path", setupOpts) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`"some path": no such file or directory`)) - }) - }) - - It("setup without container ID", func() { - runTest(func() { - defNet := types.DefaultNetworkName - intName := "eth0" - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: "", - Networks: map[string]types.PerNetworkOptions{ - defNet: { - InterfaceName: intName, - }, - }, - }, - } - _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("ContainerID is empty")) - }) - }) - - It("setup without networks", func() { - runTest(func() { - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - }, - } - _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("must specify at least one network")) - }) - }) - - It("setup without interface name", func() { - runTest(func() { - defNet := types.DefaultNetworkName - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - defNet: { - InterfaceName: "", - }, - }, - }, - } - _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("interface name on network %s is empty", defNet)) - }) - }) - - It("setup does teardown on failure", func() { - runTest(func() { - subnet1, _ := types.ParseCIDR("192.168.0.0/24") - network := types.Network{ - Subnets: []types.Subnet{ - {Subnet: subnet1}, - }, - } - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - - subnet2, _ := types.ParseCIDR("192.168.1.0/31") - network = types.Network{ - Subnets: []types.Subnet{ - {Subnet: subnet2}, - }, - } - network2, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - - intName1 := "eth0" - intName2 := "eth1" - netName1 := network1.Name - netName2 := network2.Name - - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - netName1: { - InterfaceName: intName1, - }, - netName2: { - InterfaceName: intName2, - }, - }, - }, - } - _, err = libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("Network 192.168.1.0/31 too small to allocate from")) - // Note: we call teardown on the failing net and log the error, it should be the same. - logString := logBuffer.String() - Expect(logString).To(ContainSubstring("Network 192.168.1.0/31 too small to allocate from")) - - // check in the container namespace that no interface is there - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - _, err := net.InterfaceByName(intName1) - Expect(err).To(HaveOccurred()) - - // Note: We can check if intName2 is removed because - // the cni plugin fails before it removes the interface - - // check loopback adapter - i, err := net.InterfaceByName("lo") - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal("lo")) - Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) - Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") - return nil - }) - Expect(err).ToNot(HaveOccurred()) - }) - }) - - It("setup with exposed invalid port protocol", func() { - runTest(func() { - defNet := types.DefaultNetworkName - intName := "eth0" - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - PortMappings: []types.PortMapping{{ - Protocol: "someproto", - HostIP: "127.0.0.1", - HostPort: 5000, - ContainerPort: 5000, - }}, - Networks: map[string]types.PerNetworkOptions{ - defNet: {InterfaceName: intName}, - }, - }, - } - _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("unknown port protocol someproto")) - }) - }) - - It("setup with exposed empty port protocol", func() { - runTest(func() { - defNet := types.DefaultNetworkName - intName := "eth0" - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - PortMappings: []types.PortMapping{{ - Protocol: "", - HostIP: "127.0.0.1", - HostPort: 5000, - ContainerPort: 5000, - }}, - Networks: map[string]types.PerNetworkOptions{ - defNet: {InterfaceName: intName}, - }, - }, - } - _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("port protocol should not be empty")) - }) - }) - - It("setup with unknown network", func() { - runTest(func() { - defNet := "somenet" - intName := "eth0" - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - defNet: {InterfaceName: intName}, - }, - }, - } - _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("unable to find network with name or ID somenet: network not found")) - }) - }) - - It("teardown with unknown network", func() { - runTest(func() { - interfaceName := "eth0" - netName := "somenet" - teardownOpts := types.TeardownOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - netName: { - InterfaceName: interfaceName, - }, - }, - }, - } - - err := libpodNet.Teardown(netNSContainer.Path(), teardownOpts) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("network somenet: network not found")) - logString := logBuffer.String() - Expect(logString).To(ContainSubstring("Failed to load cached network config")) - }) - }) - - It("teardown on not connected network", func() { - runTest(func() { - network := types.Network{} - network1, err := libpodNet.NetworkCreate(network, nil) - Expect(err).ToNot(HaveOccurred()) - - interfaceName := "eth0" - netName := network1.Name - teardownOpts := types.TeardownOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - netName: { - InterfaceName: interfaceName, - }, - }, - }, - } - - // Most CNI plugins do not error on teardown when there is nothing to do. - err = libpodNet.Teardown(netNSContainer.Path(), teardownOpts) - Expect(err).ToNot(HaveOccurred()) - logString := logBuffer.String() - Expect(logString).To(ContainSubstring("Failed to load cached network config")) - }) - }) - }) -}) - -func runNetListener(wg *sync.WaitGroup, protocol, ip string, port int, expectedData string) { - switch protocol { - case "tcp": - ln, err := net.Listen(protocol, net.JoinHostPort(ip, strconv.Itoa(port))) - Expect(err).ToNot(HaveOccurred()) - // make sure to read in a separate goroutine to not block - go func() { - defer GinkgoRecover() - defer wg.Done() - conn, err := ln.Accept() - Expect(err).ToNot(HaveOccurred()) - err = conn.SetDeadline(time.Now().Add(1 * time.Second)) - Expect(err).ToNot(HaveOccurred()) - data, err := io.ReadAll(conn) - Expect(err).ToNot(HaveOccurred()) - Expect(string(data)).To(Equal(expectedData)) - conn.Close() - ln.Close() - }() - case "udp": - conn, err := net.ListenUDP("udp", &net.UDPAddr{ - IP: net.ParseIP(ip), - Port: port, - }) - Expect(err).ToNot(HaveOccurred()) - err = conn.SetDeadline(time.Now().Add(1 * time.Second)) - Expect(err).ToNot(HaveOccurred()) - go func() { - defer GinkgoRecover() - defer wg.Done() - data := make([]byte, len(expectedData)) - i, err := conn.Read(data) - Expect(err).ToNot(HaveOccurred()) - Expect(i).To(Equal(len(expectedData))) - Expect(string(data)).To(Equal(expectedData)) - conn.Close() - }() - default: - Fail("unsupported protocol") - } -} diff --git a/common/libnetwork/cni/testfiles/invalid/broken.conflist b/common/libnetwork/cni/testfiles/invalid/broken.conflist deleted file mode 100644 index e5bf48b396..0000000000 --- a/common/libnetwork/cni/testfiles/invalid/broken.conflist +++ /dev/null @@ -1,25 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "bridge", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman9", - "isGateway": true, - "ipMasq": true, - "hairpinMode": true, - "ipam": { - "type": "host-local", - "routes": [ - { - "dst": "0.0.0.0/0" - } - ], - "ranges": [ - [ - { - "subnet": "10.89.8.0/24", - "gateway": "10.89.8.1" - } - ] - ] diff --git a/common/libnetwork/cni/testfiles/invalid/invalid_gateway.conflist b/common/libnetwork/cni/testfiles/invalid/invalid_gateway.conflist deleted file mode 100644 index 0e1e72fd78..0000000000 --- a/common/libnetwork/cni/testfiles/invalid/invalid_gateway.conflist +++ /dev/null @@ -1,44 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "invalidgw", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman8", - "isGateway": true, - "ipMasq": true, - "hairpinMode": true, - "ipam": { - "type": "host-local", - "routes": [ - { - "dst": "0.0.0.0/0" - } - ], - "ranges": [ - [ - { - "subnet": "10.89.8.0/24", - "gateway": "10.89.8", - "rangeStart": "10.89.8.20", - "rangeEnd": "10.89.8.50" - } - ] - ] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall", - "backend": "" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/cni/testfiles/invalid/invalidname.conflist b/common/libnetwork/cni/testfiles/invalid/invalidname.conflist deleted file mode 100644 index 2ef48ff79e..0000000000 --- a/common/libnetwork/cni/testfiles/invalid/invalidname.conflist +++ /dev/null @@ -1,42 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "bridge@123", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman9", - "isGateway": true, - "ipMasq": true, - "hairpinMode": true, - "ipam": { - "type": "host-local", - "routes": [ - { - "dst": "0.0.0.0/0" - } - ], - "ranges": [ - [ - { - "subnet": "10.89.8.0/24", - "gateway": "10.89.8.1" - } - ] - ] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall", - "backend": "" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/cni/testfiles/invalid/noname.conflist b/common/libnetwork/cni/testfiles/invalid/noname.conflist deleted file mode 100644 index 50d21e1c04..0000000000 --- a/common/libnetwork/cni/testfiles/invalid/noname.conflist +++ /dev/null @@ -1,41 +0,0 @@ -{ - "cniVersion": "0.4.0", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman9", - "isGateway": true, - "ipMasq": true, - "hairpinMode": true, - "ipam": { - "type": "host-local", - "routes": [ - { - "dst": "0.0.0.0/0" - } - ], - "ranges": [ - [ - { - "subnet": "10.89.8.0/24", - "gateway": "10.89.8.1" - } - ] - ] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall", - "backend": "" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/cni/testfiles/invalid/noplugin.conflist b/common/libnetwork/cni/testfiles/invalid/noplugin.conflist deleted file mode 100644 index af192adcab..0000000000 --- a/common/libnetwork/cni/testfiles/invalid/noplugin.conflist +++ /dev/null @@ -1,5 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "bridge", - "plugins": [] -} diff --git a/common/libnetwork/cni/testfiles/invalid/samename1.conflist b/common/libnetwork/cni/testfiles/invalid/samename1.conflist deleted file mode 100644 index f5f03ea495..0000000000 --- a/common/libnetwork/cni/testfiles/invalid/samename1.conflist +++ /dev/null @@ -1,42 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "bridge", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman9", - "isGateway": true, - "ipMasq": true, - "hairpinMode": true, - "ipam": { - "type": "host-local", - "routes": [ - { - "dst": "0.0.0.0/0" - } - ], - "ranges": [ - [ - { - "subnet": "10.89.8.0/24", - "gateway": "10.89.8.1" - } - ] - ] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall", - "backend": "" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/cni/testfiles/invalid/samename2.conflist b/common/libnetwork/cni/testfiles/invalid/samename2.conflist deleted file mode 100644 index f5f03ea495..0000000000 --- a/common/libnetwork/cni/testfiles/invalid/samename2.conflist +++ /dev/null @@ -1,42 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "bridge", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman9", - "isGateway": true, - "ipMasq": true, - "hairpinMode": true, - "ipam": { - "type": "host-local", - "routes": [ - { - "dst": "0.0.0.0/0" - } - ], - "ranges": [ - [ - { - "subnet": "10.89.8.0/24", - "gateway": "10.89.8.1" - } - ] - ] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall", - "backend": "" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/cni/testfiles/valid/87-podman.conflist b/common/libnetwork/cni/testfiles/valid/87-podman.conflist deleted file mode 100644 index ef760a61be..0000000000 --- a/common/libnetwork/cni/testfiles/valid/87-podman.conflist +++ /dev/null @@ -1,37 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "podman", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman0", - "isGateway": true, - "ipMasq": true, - "hairpinMode": true, - "ipam": { - "type": "host-local", - "routes": [{ "dst": "0.0.0.0/0" }], - "ranges": [ - [ - { - "subnet": "10.88.0.0/16", - "gateway": "10.88.0.1" - } - ] - ] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/cni/testfiles/valid/bridge.conflist b/common/libnetwork/cni/testfiles/valid/bridge.conflist deleted file mode 100644 index a954577e2c..0000000000 --- a/common/libnetwork/cni/testfiles/valid/bridge.conflist +++ /dev/null @@ -1,44 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "bridge", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman9", - "isGateway": true, - "ipMasq": true, - "hairpinMode": true, - "ipam": { - "type": "host-local", - "routes": [ - { - "dst": "0.0.0.0/0" - } - ], - "ranges": [ - [ - { - "subnet": "10.89.8.0/24", - "gateway": "10.89.8.1", - "rangeStart": "10.89.8.20", - "rangeEnd": "10.89.8.50" - } - ] - ] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall", - "backend": "" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/cni/testfiles/valid/dualstack.conflist b/common/libnetwork/cni/testfiles/valid/dualstack.conflist deleted file mode 100644 index c9e4f4d3f9..0000000000 --- a/common/libnetwork/cni/testfiles/valid/dualstack.conflist +++ /dev/null @@ -1,51 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "dualstack", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman21", - "isGateway": true, - "ipMasq": true, - "hairpinMode": true, - "ipam": { - "type": "host-local", - "routes": [ - { - "dst": "::/0" - }, - { - "dst": "0.0.0.0/0" - } - ], - "ranges": [ - [ - { - "subnet": "fd10:88:a::/64", - "gateway": "fd10:88:a::1" - } - ], - [ - { - "subnet": "10.89.19.0/24", - "gateway": "10.89.19.10" - } - ] - ] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall", - "backend": "" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/cni/testfiles/valid/internal.conflist b/common/libnetwork/cni/testfiles/valid/internal.conflist deleted file mode 100644 index 1b6f15a966..0000000000 --- a/common/libnetwork/cni/testfiles/valid/internal.conflist +++ /dev/null @@ -1,40 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "internal", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman8", - "isGateway": false, - "hairpinMode": true, - "ipam": { - "type": "host-local", - "routes": [ - { - "dst": "0.0.0.0/0" - } - ], - "ranges": [ - [ - { - "subnet": "10.89.7.0/24" - } - ] - ] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall", - "backend": "" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/cni/testfiles/valid/ipam-empty.conflist b/common/libnetwork/cni/testfiles/valid/ipam-empty.conflist deleted file mode 100644 index 74c398a9f2..0000000000 --- a/common/libnetwork/cni/testfiles/valid/ipam-empty.conflist +++ /dev/null @@ -1,27 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "ipam-empty", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman124", - "isGateway": true, - "ipMasq": true, - "hairpinMode": true, - "ipam": {} - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall", - "backend": "" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/cni/testfiles/valid/ipam-none.conflist b/common/libnetwork/cni/testfiles/valid/ipam-none.conflist deleted file mode 100644 index b8fdc7b844..0000000000 --- a/common/libnetwork/cni/testfiles/valid/ipam-none.conflist +++ /dev/null @@ -1,26 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "ipam-none", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman123", - "isGateway": true, - "ipMasq": true, - "hairpinMode": true - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall", - "backend": "" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/cni/testfiles/valid/ipam-static.conflist b/common/libnetwork/cni/testfiles/valid/ipam-static.conflist deleted file mode 100644 index 821b33a3bc..0000000000 --- a/common/libnetwork/cni/testfiles/valid/ipam-static.conflist +++ /dev/null @@ -1,42 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "ipam-static", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman125", - "isGateway": true, - "ipMasq": true, - "hairpinMode": true, - "ipam": { - "type": "static", - "routes": [ - { - "dst": "0.0.0.0/0" - } - ], - "addresses": [ - [ - { - "subnet": "10.89.0.89/16", - "gateway": "10.89.0.1" - } - ] - ] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall", - "backend": "" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/cni/testfiles/valid/isolate.conflist b/common/libnetwork/cni/testfiles/valid/isolate.conflist deleted file mode 100644 index a701931f5b..0000000000 --- a/common/libnetwork/cni/testfiles/valid/isolate.conflist +++ /dev/null @@ -1,46 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "isolate", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman123", - "isGateway": true, - "ipMasq": true, - "hairpinMode": true, - "ipam": { - "type": "host-local", - "routes": [ - { - "dst": "0.0.0.0/0" - } - ], - "ranges": [ - [ - { - "subnet": "10.0.0.0/24", - "gateway": "10.0.0.1" - } - ] - ] - }, - "capabilities": { - "ips": true - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall", - "backend": "", - "ingressPolicy": "same-bridge" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/cni/testfiles/valid/label.conflist b/common/libnetwork/cni/testfiles/valid/label.conflist deleted file mode 100644 index 9fd346b09b..0000000000 --- a/common/libnetwork/cni/testfiles/valid/label.conflist +++ /dev/null @@ -1,47 +0,0 @@ -{ - "args": { - "podman_labels": { - "mykey": "value" - } - }, - "cniVersion": "0.4.0", - "name": "label", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman15", - "isGateway": true, - "ipMasq": true, - "hairpinMode": true, - "ipam": { - "type": "host-local", - "routes": [ - { - "dst": "0.0.0.0/0" - } - ], - "ranges": [ - [ - { - "subnet": "10.89.13.0/24", - "gateway": "10.89.13.1" - } - ] - ] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall", - "backend": "" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/cni/testfiles/valid/macvlan.conflist b/common/libnetwork/cni/testfiles/valid/macvlan.conflist deleted file mode 100644 index 8f36923343..0000000000 --- a/common/libnetwork/cni/testfiles/valid/macvlan.conflist +++ /dev/null @@ -1,13 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "macvlan", - "plugins": [ - { - "type": "macvlan", - "master": "lo", - "ipam": { - "type": "dhcp" - } - } - ] -} diff --git a/common/libnetwork/cni/testfiles/valid/macvlan_mtu.conflist b/common/libnetwork/cni/testfiles/valid/macvlan_mtu.conflist deleted file mode 100644 index 2fd259117e..0000000000 --- a/common/libnetwork/cni/testfiles/valid/macvlan_mtu.conflist +++ /dev/null @@ -1,14 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "macvlan_mtu", - "plugins": [ - { - "type": "macvlan", - "master": "lo", - "ipam": { - "type": "dhcp" - }, - "mtu": 1300 - } - ] -} diff --git a/common/libnetwork/cni/testfiles/valid/mtu.conflist b/common/libnetwork/cni/testfiles/valid/mtu.conflist deleted file mode 100644 index a59d378e5f..0000000000 --- a/common/libnetwork/cni/testfiles/valid/mtu.conflist +++ /dev/null @@ -1,42 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "mtu", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman13", - "isGateway": true, - "ipMasq": true, - "mtu": 1500, - "hairpinMode": true, - "ipam": { - "type": "host-local", - "routes": [ - { - "dst": "0.0.0.0/0" - } - ], - "ranges": [ - [ - { - "subnet": "10.89.11.0/24" - } - ] - ] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall", - "backend": "" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/cni/testfiles/valid/vlan.conflist b/common/libnetwork/cni/testfiles/valid/vlan.conflist deleted file mode 100644 index 2a2346ce74..0000000000 --- a/common/libnetwork/cni/testfiles/valid/vlan.conflist +++ /dev/null @@ -1,43 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "vlan", - "plugins": [ - { - "type": "bridge", - "bridge": "cni-podman14", - "isGateway": true, - "ipMasq": true, - "hairpinMode": true, - "vlan": 5, - "ipam": { - "type": "host-local", - "routes": [ - { - "dst": "0.0.0.0/0" - } - ], - "ranges": [ - [ - { - "subnet": "10.89.12.0/24", - "gateway": "10.89.12.1" - } - ] - ] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - }, - { - "type": "firewall", - "backend": "" - }, - { - "type": "tuning" - } - ] -} diff --git a/common/libnetwork/internal/rootlessnetns/netns.go b/common/libnetwork/internal/rootlessnetns/netns.go index edc29f66fe..34d2345ee1 100644 --- a/common/libnetwork/internal/rootlessnetns/netns.go +++ b/common/libnetwork/internal/rootlessnetns/netns.go @@ -4,5 +4,4 @@ type NetworkBackend int const ( Netavark NetworkBackend = iota - CNI ) diff --git a/common/libnetwork/internal/rootlessnetns/netns_linux.go b/common/libnetwork/internal/rootlessnetns/netns_linux.go index 05b3b16dd6..bdaa9e3fe3 100644 --- a/common/libnetwork/internal/rootlessnetns/netns_linux.go +++ b/common/libnetwork/internal/rootlessnetns/netns_linux.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io/fs" - "net" "os" "path/filepath" "strconv" @@ -18,13 +17,11 @@ import ( "github.com/sirupsen/logrus" "go.podman.io/common/libnetwork/pasta" "go.podman.io/common/libnetwork/resolvconf" - "go.podman.io/common/libnetwork/slirp4netns" "go.podman.io/common/libnetwork/types" "go.podman.io/common/pkg/config" "go.podman.io/common/pkg/netns" "go.podman.io/common/pkg/systemd" "go.podman.io/storage/pkg/fileutils" - "go.podman.io/storage/pkg/homedir" "go.podman.io/storage/pkg/lockfile" "golang.org/x/sys/unix" ) @@ -38,12 +35,9 @@ const ( // infoCacheFile file name for the cache file used to store the rootless netns info. infoCacheFile = "info.json" - // rootlessNetNsConnPidFile is the name of the rootless netns slirp4netns/pasta pid file. + // rootlessNetNsConnPidFile is the name of the rootless netns pasta pid file. rootlessNetNsConnPidFile = "rootless-netns-conn.pid" - // persistentCNIDir is the directory where the CNI files are stored. - persistentCNIDir = "/var/lib/cni" - tmpfs = "tmpfs" none = "none" resolvConfName = "resolv.conf" @@ -114,7 +108,7 @@ func (n *Netns) getOrCreateNetns() (ns.NetNS, bool, error) { pidPath := n.getPath(rootlessNetNsConnPidFile) pid, err := readPidFile(pidPath) if err == nil { - // quick check if pasta/slirp4netns are still running + // quick check if pasta is still running err := unix.Kill(pid, 0) if err == nil { if err := n.deserializeInfo(); err != nil { @@ -156,14 +150,12 @@ func (n *Netns) getOrCreateNetns() (ns.NetNS, bool, error) { } } switch strings.ToLower(n.config.Network.DefaultRootlessNetworkCmd) { - case "", slirp4netns.BinaryName: - err = n.setupSlirp4netns(nsPath) - case pasta.BinaryName: + case "", pasta.BinaryName: err = n.setupPasta(nsPath) default: err = fmt.Errorf("invalid rootless network command %q", n.config.Network.DefaultRootlessNetworkCmd) } - // If pasta or slirp4netns fail here we need to get rid of the netns again to not leak it, + // If pasta fails here we need to get rid of the netns again to not leak it, // otherwise the next command thinks the netns was successfully setup. if err != nil { if nerr := netns.UnmountNS(nsPath); nerr != nil { @@ -222,7 +214,7 @@ func (n *Netns) setupPasta(nsPath string) error { return fmt.Errorf("unable to decode pasta PID: %w", err) } - if err := systemd.MoveRootlessNetnsSlirpProcessToUserSlice(pid); err != nil { + if err := systemd.MoveRootlessNetnsProcessToUserSlice(pid); err != nil { // only log this, it is not fatal but can lead to issues when running podman inside systemd units logrus.Errorf("failed to move the rootless netns pasta process to the systemd user.slice: %v", err) } @@ -253,68 +245,6 @@ func (n *Netns) setupPasta(nsPath string) error { return nil } -func (n *Netns) setupSlirp4netns(nsPath string) error { - res, err := slirp4netns.Setup(&slirp4netns.SetupOptions{ - Config: n.config, - ContainerID: "rootless-netns", - Netns: nsPath, - }) - if err != nil { - return wrapError("start slirp4netns", err) - } - // create pid file for the slirp4netns process - // this is need to kill the process in the cleanup - pid := strconv.Itoa(res.Pid) - err = os.WriteFile(n.getPath(rootlessNetNsConnPidFile), []byte(pid), 0o600) - if err != nil { - return wrapError("write slirp4netns pid file", err) - } - - if systemd.RunsOnSystemd() { - // move to systemd scope to prevent systemd from killing it - err = systemd.MoveRootlessNetnsSlirpProcessToUserSlice(res.Pid) - if err != nil { - // only log this, it is not fatal but can lead to issues when running podman inside systemd units - logrus.Errorf("failed to move the rootless netns slirp4netns process to the systemd user.slice: %v", err) - } - } - - // build a new resolv.conf file which uses the slirp4netns dns server address - resolveIP, err := slirp4netns.GetDNS(res.Subnet) - if err != nil { - return wrapError("determine default slirp4netns DNS address", err) - } - nameservers := []string{resolveIP.String()} - - netnsIP, err := slirp4netns.GetIP(res.Subnet) - if err != nil { - return wrapError("determine default slirp4netns ip address", err) - } - - if err := resolvconf.New(&resolvconf.Params{ - Path: n.getPath(resolvConfName), - // fake the netns since we want to filter localhost - Namespaces: []specs.LinuxNamespace{ - {Type: specs.NetworkNamespace}, - }, - IPv6Enabled: res.IPv6, - KeepHostServers: true, - Nameservers: nameservers, - }); err != nil { - return wrapError("create resolv.conf", err) - } - - n.info = &types.RootlessNetnsInfo{ - IPAddresses: []net.IP{*netnsIP}, - DnsForwardIps: nameservers, - } - if err := n.serializeInfo(); err != nil { - return wrapError("serialize info", err) - } - - return nil -} - func (n *Netns) cleanupRootlessNetns() error { pidFile := n.getPath(rootlessNetNsConnPidFile) pid, err := readPidFile(pidFile) @@ -324,7 +254,7 @@ func (n *Netns) cleanupRootlessNetns() error { return nil } if err == nil { - // kill the slirp/pasta process so we do not leak it + // kill the pasta process so we do not leak it err = unix.Kill(pid, unix.SIGTERM) if err == unix.ESRCH { err = nil @@ -354,11 +284,9 @@ func (n *Netns) setupMounts() error { // Because the plugins also need access to XDG_RUNTIME_DIR/netns some special setup is needed. // The following bind mounts are needed - // 1. XDG_RUNTIME_DIR -> XDG_RUNTIME_DIR/rootless-netns/XDG_RUNTIME_DIR - // 2. /run/systemd -> XDG_RUNTIME_DIR/rootless-netns/run/systemd (only if it exists) - // 3. XDG_RUNTIME_DIR/rootless-netns/resolv.conf -> /etc/resolv.conf or XDG_RUNTIME_DIR/rootless-netns/run/symlink/target - // 4. XDG_RUNTIME_DIR/rootless-netns/var/lib/cni -> /var/lib/cni (if /var/lib/cni does not exist, use the parent dir) - // 5. XDG_RUNTIME_DIR/rootless-netns/run -> /run + // 1. /run/systemd -> XDG_RUNTIME_DIR/rootless-netns/run/systemd (only if it exists) + // 2. XDG_RUNTIME_DIR/rootless-netns/resolv.conf -> /etc/resolv.conf or XDG_RUNTIME_DIR/rootless-netns/run/symlink/target + // 3. XDG_RUNTIME_DIR/rootless-netns/run -> /run // Create a new mount namespace, // this must happen inside the netns thread. @@ -379,19 +307,7 @@ func (n *Netns) setupMounts() error { return wrapError("set mount propagation to slave in new mount namespace", err) } - xdgRuntimeDir, err := homedir.GetRuntimeDir() - if err != nil { - return fmt.Errorf("could not get runtime directory: %w", err) - } - newXDGRuntimeDir := n.getPath(xdgRuntimeDir) - // 1. Mount the netns into the new run to keep them accessible. - // Otherwise cni setup will fail because it cannot access the netns files. - err = mountAndMkdirDest(xdgRuntimeDir, newXDGRuntimeDir, none, unix.MS_BIND|unix.MS_REC) - if err != nil { - return err - } - - // 2. Also keep /run/systemd if it exists. + // 1. Also keep /run/systemd if it exists. // Many files are symlinked into this dir, for example /dev/log. runSystemd := "/run/systemd" err = fileutils.Exists(runSystemd) @@ -403,7 +319,7 @@ func (n *Netns) setupMounts() error { } } - // 3. On some distros /etc/resolv.conf is symlinked to somewhere under /run. + // 2. On some distros /etc/resolv.conf is symlinked to somewhere under /run. // Because the kernel will follow the symlink before mounting, it is not // possible to mount a file at /etc/resolv.conf. We have to ensure that // the link target will be available in the mount ns. @@ -501,14 +417,7 @@ func (n *Netns) setupMounts() error { return wrapError(fmt.Sprintf("mount resolv.conf to %q", resolvePath), err) } - // 4. CNI plugins need access to /var/lib/cni - if n.backend == CNI { - if err := n.mountCNIVarDir(); err != nil { - return err - } - } - - // 5. Mount the new prepared run dir to /run, it has to be recursive to keep the other bind mounts. + // 3. Mount the new prepared run dir to /run, it has to be recursive to keep the other bind mounts. runDir := n.getPath("run") err = os.MkdirAll(runDir, 0o700) if err != nil { @@ -530,36 +439,6 @@ func (n *Netns) setupMounts() error { return nil } -func (n *Netns) mountCNIVarDir() error { - varDir := "" - varTarget := persistentCNIDir - // we can only mount to a target dir which exists, check /var/lib/cni recursively - // while we could always use /var there are cases where a user might store the cni - // configs under /var/custom and this would break - for { - if err := fileutils.Exists(varTarget); err == nil { - varDir = n.getPath(varTarget) - break - } - varTarget = filepath.Dir(varTarget) - if varTarget == "/" { - break - } - } - if varDir == "" { - return errors.New("failed to stat /var directory") - } - if err := os.MkdirAll(varDir, 0o700); err != nil { - return wrapError("create var dir", err) - } - // make sure to mount var first - err := unix.Mount(varDir, varTarget, none, unix.MS_BIND, "") - if err != nil { - return wrapError(fmt.Sprintf("mount %q to %q", varDir, varTarget), err) - } - return nil -} - func (n *Netns) runInner(toRun func() error, cleanup bool) (err error) { nsRef, newNs, err := n.getOrCreateNetns() if err != nil { diff --git a/common/libnetwork/network/interface.go b/common/libnetwork/network/interface.go index 82955b6edf..c5a329b3d3 100644 --- a/common/libnetwork/network/interface.go +++ b/common/libnetwork/network/interface.go @@ -28,11 +28,9 @@ const ( ) // NetworkBackend returns the network backend name and interface -// It returns either the CNI or netavark backend depending on what is set in the config. -// If the backend is set to "" we will automatically assign the backend on the following conditions: -// 1. read ${graphroot}/defaultNetworkBackend -// 2. find netavark binary (if not installed use CNI) -// 3. check containers, images and CNI networks and if there are some we have an existing install and should continue to use CNI +// It returns netavark backend depending on what is set in the config. +// If the backend is set to "" we will automatically assign the netavark +// backend. // // revive does not like the name because the package is already called network // @@ -41,7 +39,7 @@ func NetworkBackend(store storage.Store, conf *config.Config, syslog bool) (type backend := types.NetworkBackend(conf.Network.NetworkBackend) if backend == "" { var err error - backend, err = defaultNetworkBackend(store, conf) + backend, err = defaultNetworkBackend(store) if err != nil { return "", nil, fmt.Errorf("failed to get default network backend: %w", err) } @@ -50,6 +48,19 @@ func NetworkBackend(store storage.Store, conf *config.Config, syslog bool) (type return backendFromType(backend, store, conf, syslog) } +func backendFromType(backend types.NetworkBackend, store storage.Store, conf *config.Config, syslog bool) (types.NetworkBackend, types.ContainerNetwork, error) { + switch backend { + case types.Netavark: + netInt, err := netavarkBackendFromConf(store, conf, syslog) + if err != nil { + return "", nil, err + } + return types.Netavark, netInt, err + default: + return "", nil, fmt.Errorf("unsupported network backend %q, check network_backend in containers.conf", backend) + } +} + func netavarkBackendFromConf(store storage.Store, conf *config.Config, syslog bool) (types.ContainerNetwork, error) { netavarkBin, err := conf.FindHelperBinary(netavarkBinary, false) if err != nil { @@ -83,7 +94,7 @@ func netavarkBackendFromConf(store storage.Store, conf *config.Config, syslog bo return netInt, err } -func defaultNetworkBackend(store storage.Store, conf *config.Config) (backend types.NetworkBackend, err error) { +func defaultNetworkBackend(store storage.Store) (backend types.NetworkBackend, err error) { err = nil file := filepath.Join(store.GraphRoot(), defaultNetworkBackendFileName) @@ -102,24 +113,13 @@ func defaultNetworkBackend(store storage.Store, conf *config.Config) (backend ty if err == nil { val := string(b) - // if the network backend has been already set previously, - // handle the values depending on whether CNI is supported and - // whether the network backend is explicitly configured - if val == string(types.Netavark) { + // set network backend to netavark if not already. + if val != string(types.Netavark) { // netavark is always good - return types.Netavark, nil - } else if val == string(types.CNI) { - if cniSupported { - return types.CNI, nil - } - // the user has *not* configured a network - // backend explicitly but used CNI in the past - // => we upgrade them in this case to netavark only writeBackendToFile(types.Netavark) logrus.Info("Migrating network backend to netavark as no backend has been configured previously") - return types.Netavark, nil } - return "", fmt.Errorf("unknown network backend value %q in %q", val, file) + return types.Netavark, nil } // fail for all errors except ENOENT @@ -127,14 +127,7 @@ func defaultNetworkBackend(store storage.Store, conf *config.Config) (backend ty return "", fmt.Errorf("could not read network backend value: %w", err) } - backend, err = networkBackendFromStore(store, conf) - if err != nil { - return "", err - } - // cache the network backend to make sure always the same one will be used - writeBackendToFile(backend) - - return backend, nil + return "", nil } // getDefaultNetavarkConfigDir return the netavark config dir. For rootful it will diff --git a/common/libnetwork/network/interface_cni.go b/common/libnetwork/network/interface_cni.go deleted file mode 100644 index a46ff25182..0000000000 --- a/common/libnetwork/network/interface_cni.go +++ /dev/null @@ -1,119 +0,0 @@ -//go:build (linux || freebsd) && cni - -package network - -import ( - "errors" - "fmt" - "os" - "path/filepath" - - "go.podman.io/common/libnetwork/cni" - "go.podman.io/common/libnetwork/types" - "go.podman.io/common/pkg/config" - "go.podman.io/common/pkg/machine" - "go.podman.io/storage" - "go.podman.io/storage/pkg/homedir" - "go.podman.io/storage/pkg/unshare" -) - -const ( - // cniConfigDirRootless is the directory in XDG_CONFIG_HOME for cni plugins. - cniConfigDirRootless = "cni/net.d/" - - cniSupported = true -) - -func getCniInterface(conf *config.Config) (types.ContainerNetwork, error) { - confDir := conf.Network.NetworkConfigDir - if confDir == "" { - var err error - confDir, err = getDefaultCNIConfigDir() - if err != nil { - return nil, err - } - } - return cni.NewCNINetworkInterface(&cni.InitConfig{ - Config: conf, - CNIConfigDir: confDir, - RunDir: conf.Engine.TmpDir, - IsMachine: machine.IsGvProxyBased(), - }) -} - -func getDefaultCNIConfigDir() (string, error) { - if !unshare.IsRootless() { - return cniConfigDir, nil - } - - configHome, err := homedir.GetConfigHome() - if err != nil { - return "", err - } - - return filepath.Join(configHome, cniConfigDirRootless), nil -} - -func networkBackendFromStore(store storage.Store, conf *config.Config) (backend types.NetworkBackend, err error) { - _, err = conf.FindHelperBinary("netavark", false) - if err != nil { - // if we cannot find netavark use CNI - return types.CNI, nil - } - - // If there are any containers then return CNI - cons, err := store.Containers() - if err != nil { - return "", err - } - if len(cons) != 0 { - return types.CNI, nil - } - - // If there are any non ReadOnly images then return CNI - imgs, err := store.Images() - if err != nil { - return "", err - } - for _, i := range imgs { - if !i.ReadOnly { - return types.CNI, nil - } - } - - // If there are CNI Networks then return CNI - cniInterface, err := getCniInterface(conf) - if err == nil { - nets, err := cniInterface.NetworkList() - // there is always a default network so check > 1 - if err != nil && !errors.Is(err, os.ErrNotExist) { - return "", err - } - - if len(nets) > 1 { - // we do not have a fresh system so use CNI - return types.CNI, nil - } - } - return types.Netavark, nil -} - -func backendFromType(backend types.NetworkBackend, store storage.Store, conf *config.Config, syslog bool) (types.NetworkBackend, types.ContainerNetwork, error) { - switch backend { - case types.Netavark: - netInt, err := netavarkBackendFromConf(store, conf, syslog) - if err != nil { - return "", nil, err - } - return types.Netavark, netInt, err - case types.CNI: - netInt, err := getCniInterface(conf) - if err != nil { - return "", nil, err - } - return types.CNI, netInt, err - - default: - return "", nil, fmt.Errorf("unsupported network backend %q, check network_backend in containers.conf", backend) - } -} diff --git a/common/libnetwork/network/interface_cni_unsupported.go b/common/libnetwork/network/interface_cni_unsupported.go deleted file mode 100644 index 5d94fb8cad..0000000000 --- a/common/libnetwork/network/interface_cni_unsupported.go +++ /dev/null @@ -1,30 +0,0 @@ -//go:build (linux || freebsd) && !cni - -package network - -import ( - "fmt" - - "go.podman.io/common/libnetwork/types" - "go.podman.io/common/pkg/config" - "go.podman.io/storage" -) - -const ( - cniSupported = false -) - -func networkBackendFromStore(_store storage.Store, _conf *config.Config) (backend types.NetworkBackend, err error) { - return types.Netavark, nil -} - -func backendFromType(backend types.NetworkBackend, store storage.Store, conf *config.Config, syslog bool) (types.NetworkBackend, types.ContainerNetwork, error) { - if backend != types.Netavark { - return "", nil, fmt.Errorf("cni support is not enabled in this build, only netavark. Got unsupported network backend %q", backend) - } - cn, err := netavarkBackendFromConf(store, conf, syslog) - if err != nil { - return "", nil, err - } - return types.Netavark, cn, err -} diff --git a/common/libnetwork/network/interface_freebsd.go b/common/libnetwork/network/interface_freebsd.go index 4d60b25c7c..42fa78bc71 100644 --- a/common/libnetwork/network/interface_freebsd.go +++ b/common/libnetwork/network/interface_freebsd.go @@ -1,8 +1,6 @@ package network const ( - // cniConfigDir is the directory where cni configuration is found - cniConfigDir = "/usr/local/etc/cni/net.d/" // netavarkConfigDir is the config directory for the rootful network files netavarkConfigDir = "/usr/local/etc/containers/networks" // netavarkRunDir is the run directory for the rootful temporary network files such as the ipam db diff --git a/common/libnetwork/network/interface_linux.go b/common/libnetwork/network/interface_linux.go index 7a5db69a58..f89acfd102 100644 --- a/common/libnetwork/network/interface_linux.go +++ b/common/libnetwork/network/interface_linux.go @@ -1,8 +1,6 @@ package network const ( - // cniConfigDir is the directory where cni configuration is found. - cniConfigDir = "/etc/cni/net.d/" // netavarkConfigDir is the config directory for the rootful network files. netavarkConfigDir = "/etc/containers/networks" // netavarkRunDir is the run directory for the rootful temporary network files such as the ipam db. diff --git a/common/libnetwork/slirp4netns/const.go b/common/libnetwork/slirp4netns/const.go deleted file mode 100644 index 82f3bff3a0..0000000000 --- a/common/libnetwork/slirp4netns/const.go +++ /dev/null @@ -1,17 +0,0 @@ -package slirp4netns - -import "net" - -const ( - BinaryName = "slirp4netns" -) - -// SetupResult return type from Setup(). -type SetupResult struct { - // Pid of the created slirp4netns process - Pid int - // Subnet which is used by slirp4netns - Subnet *net.IPNet - // IPv6 whenever Ipv6 is enabled in slirp4netns - IPv6 bool -} diff --git a/common/libnetwork/slirp4netns/const_linux.go b/common/libnetwork/slirp4netns/const_linux.go deleted file mode 100644 index 8e2742fe3f..0000000000 --- a/common/libnetwork/slirp4netns/const_linux.go +++ /dev/null @@ -1,11 +0,0 @@ -package slirp4netns - -const ( - ipv6ConfDefaultAcceptDadSysctl = "/proc/sys/net/ipv6/conf/default/accept_dad" - - // defaultMTU the default MTU override. - defaultMTU = 65520 - - // default slirp4ns subnet. - defaultSubnet = "10.0.2.0/24" -) diff --git a/common/libnetwork/slirp4netns/slirp4netns.go b/common/libnetwork/slirp4netns/slirp4netns.go deleted file mode 100644 index 083a4e5fcc..0000000000 --- a/common/libnetwork/slirp4netns/slirp4netns.go +++ /dev/null @@ -1,743 +0,0 @@ -//go:build linux - -package slirp4netns - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "io" - "net" - "os" - "os/exec" - "path/filepath" - "strconv" - "strings" - "sync" - "syscall" - "time" - - "github.com/containernetworking/plugins/pkg/ns" - "github.com/sirupsen/logrus" - "go.podman.io/common/libnetwork/types" - "go.podman.io/common/pkg/config" - "go.podman.io/common/pkg/rootlessport" - "go.podman.io/common/pkg/servicereaper" - "go.podman.io/common/pkg/util" -) - -type slirpFeatures struct { - HasDisableHostLoopback bool - HasMTU bool - HasEnableSandbox bool - HasEnableSeccomp bool - HasCIDR bool - HasOutboundAddr bool - HasIPv6 bool -} - -type slirp4netnsCmdArg struct { - Proto string `json:"proto,omitempty"` - HostAddr string `json:"host_addr"` - HostPort uint16 `json:"host_port"` - GuestAddr string `json:"guest_addr"` - GuestPort uint16 `json:"guest_port"` -} - -type slirp4netnsCmd struct { - Execute string `json:"execute"` - Args slirp4netnsCmdArg `json:"arguments"` -} - -type networkOptions struct { - cidr string - disableHostLoopback bool - enableIPv6 bool - isSlirpHostForward bool - noPivotRoot bool - mtu int - outboundAddr string - outboundAddr6 string -} - -type SetupOptions struct { - // Config used to get slip4netns path and other default options - Config *config.Config - // ContainerID is the ID of the container - ContainerID string - // Netns path to the netns - Netns string - // Ports the should be forwarded - Ports []types.PortMapping - // ExtraOptions for slirp4netns that were set on the cli - ExtraOptions []string - // Slirp4netnsExitPipeR pipe used to exit the slirp4netns process. - // This is must be the reading end, the writer must be kept open until you want the - // process to exit. For podman, conmon will hold the pipe open. - // It can be set to nil in which case we do not use the pipe exit and the caller - // must use the returned pid to kill the process after it is done. - Slirp4netnsExitPipeR *os.File - // RootlessPortSyncPipe pipe used to exit the rootlessport process. - // Same as Slirp4netnsExitPipeR, except this is only used when ports are given. - RootlessPortExitPipeR *os.File - // Pdeathsig is the signal which is send to slirp4netns process if the calling thread - // exits. The caller is responsible for locking the thread with runtime.LockOSThread(). - Pdeathsig syscall.Signal -} - -type logrusDebugWriter struct { - prefix string -} - -func (w *logrusDebugWriter) Write(p []byte) (int, error) { - logrus.Debugf("%s%s", w.prefix, string(p)) - return len(p), nil -} - -func checkSlirpFlags(path string) (*slirpFeatures, error) { - cmd := exec.Command(path, "--help") - out, err := cmd.CombinedOutput() - if err != nil { - return nil, fmt.Errorf("slirp4netns %q: %w", out, err) - } - return &slirpFeatures{ - HasDisableHostLoopback: strings.Contains(string(out), "--disable-host-loopback"), - HasMTU: strings.Contains(string(out), "--mtu"), - HasEnableSandbox: strings.Contains(string(out), "--enable-sandbox"), - HasEnableSeccomp: strings.Contains(string(out), "--enable-seccomp"), - HasCIDR: strings.Contains(string(out), "--cidr"), - HasOutboundAddr: strings.Contains(string(out), "--outbound-addr"), - HasIPv6: strings.Contains(string(out), "--enable-ipv6"), - }, nil -} - -func parseNetworkOptions(config *config.Config, extraOptions []string) (*networkOptions, error) { - options := make([]string, 0, len(config.Engine.NetworkCmdOptions.Get())+len(extraOptions)) - options = append(options, config.Engine.NetworkCmdOptions.Get()...) - options = append(options, extraOptions...) - opts := &networkOptions{ - // overwrite defaults - disableHostLoopback: true, - mtu: defaultMTU, - noPivotRoot: config.Engine.NoPivotRoot, - enableIPv6: true, - } - for _, o := range options { - option, value, ok := strings.Cut(o, "=") - if !ok { - return nil, fmt.Errorf("unknown option for slirp4netns: %q", o) - } - switch option { - case "cidr": - ipv4, _, err := net.ParseCIDR(value) - if err != nil || ipv4.To4() == nil { - return nil, fmt.Errorf("invalid cidr %q", value) - } - opts.cidr = value - case "port_handler": - switch value { - case "slirp4netns": - opts.isSlirpHostForward = true - case "rootlesskit": - opts.isSlirpHostForward = false - default: - return nil, fmt.Errorf("unknown port_handler for slirp4netns: %q", value) - } - case "allow_host_loopback": - switch value { - case "true": - opts.disableHostLoopback = false - case "false": - opts.disableHostLoopback = true - default: - return nil, fmt.Errorf("invalid value of allow_host_loopback for slirp4netns: %q", value) - } - case "enable_ipv6": - switch value { - case "true": - opts.enableIPv6 = true - case "false": - opts.enableIPv6 = false - default: - return nil, fmt.Errorf("invalid value of enable_ipv6 for slirp4netns: %q", value) - } - case "outbound_addr": - ipv4 := net.ParseIP(value) - if ipv4 == nil || ipv4.To4() == nil { - _, err := net.InterfaceByName(value) - if err != nil { - return nil, fmt.Errorf("invalid outbound_addr %q", value) - } - } - opts.outboundAddr = value - case "outbound_addr6": - ipv6 := net.ParseIP(value) - if ipv6 == nil || ipv6.To4() != nil { - _, err := net.InterfaceByName(value) - if err != nil { - return nil, fmt.Errorf("invalid outbound_addr6: %q", value) - } - } - opts.outboundAddr6 = value - case "mtu": - var err error - opts.mtu, err = strconv.Atoi(value) - if opts.mtu < 68 || err != nil { - return nil, fmt.Errorf("invalid mtu %q", value) - } - default: - return nil, fmt.Errorf("unknown option for slirp4netns: %q", o) - } - } - return opts, nil -} - -func createBasicSlirpCmdArgs(options *networkOptions, features *slirpFeatures) ([]string, error) { - cmdArgs := []string{} - if options.disableHostLoopback && features.HasDisableHostLoopback { - cmdArgs = append(cmdArgs, "--disable-host-loopback") - } - if options.mtu > -1 && features.HasMTU { - cmdArgs = append(cmdArgs, "--mtu="+strconv.Itoa(options.mtu)) - } - if !options.noPivotRoot && features.HasEnableSandbox { - cmdArgs = append(cmdArgs, "--enable-sandbox") - } - if features.HasEnableSeccomp { - cmdArgs = append(cmdArgs, "--enable-seccomp") - } - - if options.cidr != "" { - if !features.HasCIDR { - return nil, errors.New("cidr not supported") - } - cmdArgs = append(cmdArgs, "--cidr="+options.cidr) - } - - if options.enableIPv6 { - if !features.HasIPv6 { - return nil, errors.New("enable_ipv6 not supported") - } - cmdArgs = append(cmdArgs, "--enable-ipv6") - } - - if options.outboundAddr != "" { - if !features.HasOutboundAddr { - return nil, errors.New("outbound_addr not supported") - } - cmdArgs = append(cmdArgs, "--outbound-addr="+options.outboundAddr) - } - - if options.outboundAddr6 != "" { - if !features.HasOutboundAddr || !features.HasIPv6 { - return nil, errors.New("outbound_addr6 not supported") - } - if !options.enableIPv6 { - return nil, errors.New("enable_ipv6=true is required for outbound_addr6") - } - cmdArgs = append(cmdArgs, "--outbound-addr6="+options.outboundAddr6) - } - - return cmdArgs, nil -} - -// Setup can be called in rootful as well as in rootless. -// Spawns the slirp4netns process and setup port forwarding if ports are given. -func Setup(opts *SetupOptions) (*SetupResult, error) { - path := opts.Config.Engine.NetworkCmdPath - if path == "" { - var err error - path, err = opts.Config.FindHelperBinary(BinaryName, true) - if err != nil { - return nil, fmt.Errorf("could not find slirp4netns, the network namespace can't be configured: %w", err) - } - } - - syncR, syncW, err := os.Pipe() - if err != nil { - return nil, fmt.Errorf("failed to open pipe: %w", err) - } - defer closeQuiet(syncR) - defer closeQuiet(syncW) - - havePortMapping := len(opts.Ports) > 0 - logPath := filepath.Join(opts.Config.Engine.TmpDir, fmt.Sprintf("slirp4netns-%s.log", opts.ContainerID)) - - netOptions, err := parseNetworkOptions(opts.Config, opts.ExtraOptions) - if err != nil { - return nil, err - } - slirpFeatures, err := checkSlirpFlags(path) - if err != nil { - return nil, fmt.Errorf("checking slirp4netns binary %s: %q: %w", path, err, err) - } - cmdArgs, err := createBasicSlirpCmdArgs(netOptions, slirpFeatures) - if err != nil { - return nil, err - } - - // the slirp4netns arguments being passed are described as follows: - // from the slirp4netns documentation: https://github.com/rootless-containers/slirp4netns - // -c, --configure Brings up the tap interface - // -e, --exit-fd=FD specify the FD for terminating slirp4netns - // -r, --ready-fd=FD specify the FD to write to when the initialization steps are finished - cmdArgs = append(cmdArgs, "-c", "-r", "3") - if opts.Slirp4netnsExitPipeR != nil { - cmdArgs = append(cmdArgs, "-e", "4") - } - - var apiSocket string - if havePortMapping && netOptions.isSlirpHostForward { - apiSocket = filepath.Join(opts.Config.Engine.TmpDir, opts.ContainerID+".net") - cmdArgs = append(cmdArgs, "--api-socket", apiSocket) - } - - cmdArgs = append(cmdArgs, "--netns-type=path", opts.Netns, "tap0") - - cmd := exec.Command(path, cmdArgs...) - logrus.Debugf("slirp4netns command: %s", strings.Join(cmd.Args, " ")) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setpgid: true, - Pdeathsig: opts.Pdeathsig, - } - - // workaround for https://github.com/rootless-containers/slirp4netns/pull/153 - if !netOptions.noPivotRoot && slirpFeatures.HasEnableSandbox { - cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNS - cmd.SysProcAttr.Unshareflags = syscall.CLONE_NEWNS - } - - // Leak one end of the pipe in slirp4netns, the other will be sent to conmon - cmd.ExtraFiles = append(cmd.ExtraFiles, syncW) - if opts.Slirp4netnsExitPipeR != nil { - cmd.ExtraFiles = append(cmd.ExtraFiles, opts.Slirp4netnsExitPipeR) - } - - logFile, err := os.Create(logPath) - if err != nil { - return nil, fmt.Errorf("failed to open slirp4netns log file %s: %w", logPath, err) - } - defer logFile.Close() - // Unlink immediately the file so we won't need to worry about cleaning it up later. - // It is still accessible through the open fd logFile. - if err := os.Remove(logPath); err != nil { - return nil, fmt.Errorf("delete file %s: %w", logPath, err) - } - cmd.Stdout = logFile - cmd.Stderr = logFile - - var slirpReadyWg, netnsReadyWg *sync.WaitGroup - if netOptions.enableIPv6 { - // use two wait groups to make sure we set the sysctl before - // starting slirp and reset it only after slirp is ready - slirpReadyWg = &sync.WaitGroup{} - netnsReadyWg = &sync.WaitGroup{} - slirpReadyWg.Add(1) - netnsReadyWg.Add(1) - - go func() { - err := ns.WithNetNSPath(opts.Netns, func(_ ns.NetNS) error { - // Duplicate Address Detection slows the ipv6 setup down for 1-2 seconds. - // Since slirp4netns is run in its own namespace and not directly routed - // we can skip this to make the ipv6 address immediately available. - // We change the default to make sure the slirp tap interface gets the - // correct value assigned so DAD is disabled for it - // Also make sure to change this value back to the original after slirp4netns - // is ready in case users rely on this sysctl. - orgValue, err := os.ReadFile(ipv6ConfDefaultAcceptDadSysctl) - if err != nil { - netnsReadyWg.Done() - // on ipv6 disabled systems the sysctl does not exist - // so we should not error - if errors.Is(err, os.ErrNotExist) { - return nil - } - return err - } - err = os.WriteFile(ipv6ConfDefaultAcceptDadSysctl, []byte("0"), 0o644) - netnsReadyWg.Done() - if err != nil { - return err - } - - // wait until slirp4nets is ready before resetting this value - slirpReadyWg.Wait() - return os.WriteFile(ipv6ConfDefaultAcceptDadSysctl, orgValue, 0o644) - }) - if err != nil { - logrus.Warnf("failed to set net.ipv6.conf.default.accept_dad sysctl: %v", err) - } - }() - - // wait until we set the sysctl - netnsReadyWg.Wait() - } - - if err := cmd.Start(); err != nil { - if netOptions.enableIPv6 { - slirpReadyWg.Done() - } - return nil, fmt.Errorf("failed to start slirp4netns process: %w", err) - } - defer func() { - servicereaper.AddPID(cmd.Process.Pid) - if err := cmd.Process.Release(); err != nil { - logrus.Errorf("Unable to release command process: %q", err) - } - }() - - err = waitForSync(syncR, cmd, logFile, 1*time.Second) - if netOptions.enableIPv6 { - slirpReadyWg.Done() - } - if err != nil { - return nil, err - } - - // Set a default slirp subnet. Parsing a string with the net helper is easier than building the struct myself - _, slirpSubnet, _ := net.ParseCIDR(defaultSubnet) - - // Set slirp4netnsSubnet addresses now that we are pretty sure the command executed - if netOptions.cidr != "" { - ipv4, ipv4network, err := net.ParseCIDR(netOptions.cidr) - if err != nil || ipv4.To4() == nil { - return nil, fmt.Errorf("invalid cidr %q", netOptions.cidr) - } - slirpSubnet = ipv4network - } - - if havePortMapping { - if netOptions.isSlirpHostForward { - err = setupRootlessPortMappingViaSlirp(opts.Ports, cmd, apiSocket) - } else { - err = SetupRootlessPortMappingViaRLK(opts, slirpSubnet, nil) - } - if err != nil { - return nil, err - } - } - - return &SetupResult{ - Pid: cmd.Process.Pid, - Subnet: slirpSubnet, - IPv6: netOptions.enableIPv6, - }, nil -} - -// GetIP returns the slirp ipv4 address based on subnet. If subnet is null use default subnet. -// Reference: https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md#description -func GetIP(subnet *net.IPNet) (*net.IP, error) { - _, slirpSubnet, _ := net.ParseCIDR(defaultSubnet) - if subnet != nil { - slirpSubnet = subnet - } - expectedIP, err := addToIP(slirpSubnet, uint32(100)) - if err != nil { - return nil, fmt.Errorf("calculating expected ip for slirp4netns: %w", err) - } - return expectedIP, nil -} - -// GetGateway returns the slirp gateway ipv4 address based on subnet. -// Reference: https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md#description -func GetGateway(subnet *net.IPNet) (*net.IP, error) { - _, slirpSubnet, _ := net.ParseCIDR(defaultSubnet) - if subnet != nil { - slirpSubnet = subnet - } - expectedGatewayIP, err := addToIP(slirpSubnet, uint32(2)) - if err != nil { - return nil, fmt.Errorf("calculating expected gateway ip for slirp4netns: %w", err) - } - return expectedGatewayIP, nil -} - -// GetDNS returns slirp DNS ipv4 address based on subnet. -// Reference: https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md#description -func GetDNS(subnet *net.IPNet) (*net.IP, error) { - _, slirpSubnet, _ := net.ParseCIDR(defaultSubnet) - if subnet != nil { - slirpSubnet = subnet - } - expectedDNSIP, err := addToIP(slirpSubnet, uint32(3)) - if err != nil { - return nil, fmt.Errorf("calculating expected dns ip for slirp4netns: %w", err) - } - return expectedDNSIP, nil -} - -// Helper function to calculate slirp ip address offsets -// Adapted from: https://github.com/signalsciences/ipv4/blob/master/int.go#L12-L24 -func addToIP(subnet *net.IPNet, offset uint32) (*net.IP, error) { - // I have no idea why I have to do this, but if I don't ip is 0 - ipFixed := subnet.IP.To4() - - ipInteger := uint32(ipFixed[3]) | uint32(ipFixed[2])<<8 | uint32(ipFixed[1])<<16 | uint32(ipFixed[0])<<24 - ipNewRaw := ipInteger + offset - // Avoid overflows - if ipNewRaw < ipInteger { - return nil, fmt.Errorf("integer overflow while calculating ip address offset, %s + %d", ipFixed, offset) - } - ipNew := net.IPv4(byte(ipNewRaw>>24), byte(ipNewRaw>>16&0xFF), byte(ipNewRaw>>8)&0xFF, byte(ipNewRaw&0xFF)) - if !subnet.Contains(ipNew) { - return nil, fmt.Errorf("calculated ip address %s is not within given subnet %s", ipNew.String(), subnet.String()) - } - return &ipNew, nil -} - -func waitForSync(syncR *os.File, cmd *exec.Cmd, logFile io.ReadSeeker, timeout time.Duration) error { - prog := filepath.Base(cmd.Path) - if len(cmd.Args) > 0 { - prog = cmd.Args[0] - } - b := make([]byte, 16) - for { - if err := syncR.SetDeadline(time.Now().Add(timeout)); err != nil { - return fmt.Errorf("setting %s pipe timeout: %w", prog, err) - } - // FIXME: return err as soon as proc exits, without waiting for timeout - _, err := syncR.Read(b) - if err == nil { - break - } - if errors.Is(err, os.ErrDeadlineExceeded) { - // Check if the process is still running. - var status syscall.WaitStatus - pid, err := syscall.Wait4(cmd.Process.Pid, &status, syscall.WNOHANG, nil) - if err != nil { - return fmt.Errorf("failed to read %s process status: %w", prog, err) - } - if pid != cmd.Process.Pid { - continue - } - if status.Exited() { - // Seek at the beginning of the file and read all its content - if _, err := logFile.Seek(0, 0); err != nil { - logrus.Errorf("Could not seek log file: %q", err) - } - logContent, err := io.ReadAll(logFile) - if err != nil { - return fmt.Errorf("%s failed: %w", prog, err) - } - return fmt.Errorf("%s failed: %q", prog, logContent) - } - if status.Signaled() { - return fmt.Errorf("%s killed by signal", prog) - } - continue - } - return fmt.Errorf("failed to read from %s sync pipe: %w", prog, err) - } - return nil -} - -func SetupRootlessPortMappingViaRLK(opts *SetupOptions, slirpSubnet *net.IPNet, netStatus map[string]types.StatusBlock) error { - syncR, syncW, err := os.Pipe() - if err != nil { - return fmt.Errorf("failed to open pipe: %w", err) - } - defer closeQuiet(syncR) - defer closeQuiet(syncW) - - logPath := filepath.Join(opts.Config.Engine.TmpDir, fmt.Sprintf("rootlessport-%s.log", opts.ContainerID)) - logFile, err := os.Create(logPath) - if err != nil { - return fmt.Errorf("failed to open rootlessport log file %s: %w", logPath, err) - } - defer logFile.Close() - // Unlink immediately the file so we won't need to worry about cleaning it up later. - // It is still accessible through the open fd logFile. - if err := os.Remove(logPath); err != nil { - return fmt.Errorf("delete file %s: %w", logPath, err) - } - - childIP := GetRootlessPortChildIP(slirpSubnet, netStatus) - cfg := rootlessport.Config{ - Mappings: opts.Ports, - NetNSPath: opts.Netns, - ExitFD: 3, - ReadyFD: 4, - TmpDir: opts.Config.Engine.TmpDir, - ChildIP: childIP, - ContainerID: opts.ContainerID, - RootlessCNI: netStatus != nil, - } - cfgJSON, err := json.Marshal(cfg) - if err != nil { - return err - } - cfgR := bytes.NewReader(cfgJSON) - var stdout bytes.Buffer - path, err := opts.Config.FindHelperBinary(rootlessport.BinaryName, false) - if err != nil { - return err - } - cmd := exec.Command(path) - cmd.Args = []string{rootlessport.BinaryName} - - // Leak one end of the pipe in rootlessport process, the other will be sent to conmon - cmd.ExtraFiles = append(cmd.ExtraFiles, opts.RootlessPortExitPipeR, syncW) - cmd.Stdin = cfgR - // stdout is for human-readable error, stderr is for debug log - cmd.Stdout = &stdout - cmd.Stderr = io.MultiWriter(logFile, &logrusDebugWriter{"rootlessport: "}) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setpgid: true, - } - if err := cmd.Start(); err != nil { - return fmt.Errorf("failed to start rootlessport process: %w", err) - } - defer func() { - servicereaper.AddPID(cmd.Process.Pid) - if err := cmd.Process.Release(); err != nil { - logrus.Errorf("Unable to release rootlessport process: %q", err) - } - }() - if err := waitForSync(syncR, cmd, logFile, 3*time.Second); err != nil { - stdoutStr := stdout.String() - if stdoutStr != "" { - // err contains full debug log and too verbose, so return stdoutStr - logrus.Debug(err) - return errors.New("rootlessport " + strings.TrimSuffix(stdoutStr, "\n")) - } - return err - } - logrus.Debug("rootlessport is ready") - return nil -} - -func setupRootlessPortMappingViaSlirp(ports []types.PortMapping, cmd *exec.Cmd, apiSocket string) (err error) { - const pidWaitTimeout = 60 * time.Second - chWait := make(chan error) - go func() { - interval := 25 * time.Millisecond - for i := time.Duration(0); i < pidWaitTimeout; i += interval { - // Check if the process is still running. - var status syscall.WaitStatus - pid, err := syscall.Wait4(cmd.Process.Pid, &status, syscall.WNOHANG, nil) - if err != nil { - break - } - if pid != cmd.Process.Pid { - continue - } - if status.Exited() || status.Signaled() { - chWait <- fmt.Errorf("slirp4netns exited with status %d", status.ExitStatus()) - } - time.Sleep(interval) - } - }() - defer close(chWait) - - // wait that API socket file appears before trying to use it. - if _, err := util.WaitForFile(apiSocket, chWait, pidWaitTimeout); err != nil { - return fmt.Errorf("waiting for slirp4nets to create the api socket file %s: %w", apiSocket, err) - } - - // for each port we want to add we need to open a connection to the slirp4netns control socket - // and send the add_hostfwd command. - for _, port := range ports { - for protocol := range strings.SplitSeq(port.Protocol, ",") { - hostIP := port.HostIP - if hostIP == "" { - hostIP = "0.0.0.0" - } - for i := range port.Range { - if err := openSlirp4netnsPort(apiSocket, protocol, hostIP, port.HostPort+i, port.ContainerPort+i); err != nil { - return err - } - } - } - } - logrus.Debug("slirp4netns port-forwarding setup via add_hostfwd is ready") - return nil -} - -// openSlirp4netnsPort sends the slirp4netns pai quey to the given socket. -func openSlirp4netnsPort(apiSocket, proto, hostip string, hostport, guestport uint16) error { - conn, err := net.Dial("unix", apiSocket) - if err != nil { - return fmt.Errorf("cannot open connection to %s: %w", apiSocket, err) - } - defer func() { - if err := conn.Close(); err != nil { - logrus.Errorf("Unable to close slirp4netns connection: %q", err) - } - }() - apiCmd := slirp4netnsCmd{ - Execute: "add_hostfwd", - Args: slirp4netnsCmdArg{ - Proto: proto, - HostAddr: hostip, - HostPort: hostport, - GuestPort: guestport, - }, - } - // create the JSON payload and send it. Mark the end of request shutting down writes - // to the socket, as requested by slirp4netns. - data, err := json.Marshal(&apiCmd) - if err != nil { - return fmt.Errorf("cannot marshal JSON for slirp4netns: %w", err) - } - if _, err := fmt.Fprintf(conn, "%s\n", data); err != nil { - return fmt.Errorf("cannot write to control socket %s: %w", apiSocket, err) - } - //nolint:errcheck // This cast should never fail, if it does we get a interface - // conversion panic and a stack trace on how we ended up here which is more - // valuable than returning a human friendly error test as we don't know how it - // happened. - if err := conn.(*net.UnixConn).CloseWrite(); err != nil { - return fmt.Errorf("cannot shutdown the socket %s: %w", apiSocket, err) - } - buf := make([]byte, 2048) - readLength, err := conn.Read(buf) - if err != nil { - return fmt.Errorf("cannot read from control socket %s: %w", apiSocket, err) - } - // if there is no 'error' key in the received JSON data, then the operation was - // successful. - var y map[string]any - if err := json.Unmarshal(buf[0:readLength], &y); err != nil { - return fmt.Errorf("parsing error status from slirp4netns: %w", err) - } - if e, found := y["error"]; found { - return fmt.Errorf("from slirp4netns while setting up port redirection: %v", e) - } - return nil -} - -func GetRootlessPortChildIP(slirpSubnet *net.IPNet, netStatus map[string]types.StatusBlock) string { - if slirpSubnet != nil { - slirp4netnsIP, err := GetIP(slirpSubnet) - if err != nil { - return "" - } - return slirp4netnsIP.String() - } - - var ipv6 net.IP - for _, status := range netStatus { - for _, netInt := range status.Interfaces { - for _, netAddress := range netInt.Subnets { - ipv4 := netAddress.IPNet.IP.To4() - if ipv4 != nil { - return ipv4.String() - } - ipv6 = netAddress.IPNet.IP - } - } - } - if ipv6 != nil { - return ipv6.String() - } - return "" -} - -// closeQuiet closes a file and logs any error. Should only be used within -// a defer. -func closeQuiet(f *os.File) { - if err := f.Close(); err != nil { - logrus.Errorf("Unable to close file %s: %q", f.Name(), err) - } -} diff --git a/common/libnetwork/types/const.go b/common/libnetwork/types/const.go index d8730dcdf0..184fe92335 100644 --- a/common/libnetwork/types/const.go +++ b/common/libnetwork/types/const.go @@ -52,7 +52,6 @@ const ( type NetworkBackend string const ( - CNI NetworkBackend = "cni" Netavark NetworkBackend = "netavark" ) diff --git a/common/pkg/config/config.go b/common/pkg/config/config.go index 8bc23deba1..d304d9e801 100644 --- a/common/pkg/config/config.go +++ b/common/pkg/config/config.go @@ -49,7 +49,7 @@ type Config struct { Engine EngineConfig `toml:"engine"` // Machine specifies configurations of podman machine VMs Machine MachineConfig `toml:"machine"` - // Network section defines the configuration of CNI Plugins + // Network section defines the network configuration Network NetworkConfig `toml:"network"` // Secret section defines configurations for the secret management Secrets SecretConfig `toml:"secrets"` @@ -403,13 +403,6 @@ type EngineConfig struct { // containers and pods will be visible. The default namespace is "". Namespace string `toml:"namespace,omitempty"` - // NetworkCmdPath is the path to the slirp4netns binary. - NetworkCmdPath string `toml:"network_cmd_path,omitempty"` - - // NetworkCmdOptions is the default options to pass to the slirp4netns binary. - // For example "allow_host_loopback=true" - NetworkCmdOptions attributedstring.Slice `toml:"network_cmd_options,omitempty"` - // NoPivotRoot sets whether to set no-pivot-root in the OCI runtime. NoPivotRoot bool `toml:"no_pivot_root,omitempty"` @@ -593,9 +586,6 @@ type NetworkConfig struct { // networking. NetworkBackend string `toml:"network_backend,omitempty"` - // CNIPluginDirs is where CNI plugin binaries are stored. - CNIPluginDirs attributedstring.Slice `toml:"cni_plugin_dirs,omitempty"` - // NetavarkPluginDirs is a list of directories which contain netavark plugins. NetavarkPluginDirs attributedstring.Slice `toml:"netavark_plugin_dirs,omitempty"` @@ -620,7 +610,7 @@ type NetworkConfig struct { DefaultSubnetPools []SubnetPool `toml:"default_subnet_pools,omitempty"` // DefaultRootlessNetworkCmd is used to set the default rootless network - // program, either "slirp4nents" (default) or "pasta". + // program, currently only "pasta". DefaultRootlessNetworkCmd string `toml:"default_rootless_network_cmd,omitempty"` // NetworkConfigDir is where network configuration files are stored. diff --git a/common/pkg/config/config_local_test.go b/common/pkg/config/config_local_test.go index 81c37d6858..5de2edcf5f 100644 --- a/common/pkg/config/config_local_test.go +++ b/common/pkg/config/config_local_test.go @@ -27,26 +27,6 @@ var _ = Describe("Config Local", func() { file.Close() defer os.Remove(tmpfile) defConf.Network.NetworkConfigDir = tmpfile - defConf.Network.CNIPluginDirs.Set([]string{}) - - // When - err = defConf.Network.Validate() - - // Then - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - }) - - It("should not fail on invalid CNIPluginDirs", func() { - defConf, err := defaultConfig() - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - gomega.Expect(defConf).NotTo(gomega.BeNil()) - - t := GinkgoT() - validDirPath := t.TempDir() - - // Given - defConf.Network.NetworkConfigDir = validDirPath - defConf.Network.CNIPluginDirs.Set([]string{invalidPath}) // When err = defConf.Network.Validate() @@ -65,7 +45,6 @@ var _ = Describe("Config Local", func() { // Given defConf.Network.NetworkConfigDir = validDirPath - defConf.Network.CNIPluginDirs.Set([]string{validDirPath}) net, _ := types.ParseCIDR("10.0.0.0/24") defConf.Network.DefaultSubnetPools = []SubnetPool{ @@ -147,11 +126,6 @@ var _ = Describe("Config Local", func() { config, err := newLocked(&Options{}, &paths{}) gomega.Expect(err).ToNot(gomega.HaveOccurred()) gomega.Expect(config.Network.DefaultRootlessNetworkCmd).To(gomega.Equal("pasta")) - // When - config2, err := newLocked(&Options{}, &paths{etc: "testdata/containers_default.conf"}) - // Then - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - gomega.Expect(config2.Network.DefaultRootlessNetworkCmd).To(gomega.Equal("slirp4netns")) }) It("should fail on invalid device mode", func() { diff --git a/common/pkg/config/config_remote_test.go b/common/pkg/config/config_remote_test.go index f23eaf8c76..06c48ba3ea 100644 --- a/common/pkg/config/config_remote_test.go +++ b/common/pkg/config/config_remote_test.go @@ -8,25 +8,6 @@ import ( ) var _ = Describe("Config Remote", func() { - It("should succeed on invalid CNIPluginDirs", func() { - t := GinkgoT() - validDirPath := t.TempDir() - - // Given - defConf, err := defaultConfig() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(defConf).NotTo(gomega.BeNil()) - - defConf.Network.NetworkConfigDir = validDirPath - defConf.Network.CNIPluginDirs.Set([]string{invalidPath}) - - // When - err = defConf.Network.Validate() - - // Then - gomega.Expect(err).To(gomega.BeNil()) - }) - It("should succeed on invalid device mode", func() { // Given defConf, err := defaultConfig() @@ -119,40 +100,4 @@ var _ = Describe("Config Remote", func() { // Then gomega.Expect(err).To(gomega.BeNil()) }) - - It("should succeed on invalid CNIPluginDirs", func() { - t := GinkgoT() - validDirPath := t.TempDir() - - // Given - defConf, err := defaultConfig() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(defConf).NotTo(gomega.BeNil()) - defConf.Network.NetworkConfigDir = validDirPath - defConf.Network.CNIPluginDirs.Set([]string{invalidPath}) - - // When - err = defConf.Network.Validate() - - // Then - gomega.Expect(err).To(gomega.BeNil()) - }) - - It("should succeed in validating invalid PluginDir", func() { - t := GinkgoT() - validDirPath := t.TempDir() - - // Given - defConf, err := defaultConfig() - gomega.Expect(err).To(gomega.BeNil()) - gomega.Expect(defConf).NotTo(gomega.BeNil()) - defConf.Network.NetworkConfigDir = validDirPath - defConf.Network.CNIPluginDirs.Set([]string{invalidPath}) - - // When - err = defConf.Network.Validate() - - // Then - gomega.Expect(err).To(gomega.BeNil()) - }) }) diff --git a/common/pkg/config/config_test.go b/common/pkg/config/config_test.go index ed8d753d8b..18aad4888f 100644 --- a/common/pkg/config/config_test.go +++ b/common/pkg/config/config_test.go @@ -289,11 +289,6 @@ image_copy_tmp_dir="storage"` }, } - pluginDirs := []string{ - "/usr/libexec/cni", - "/tmp", - } - envs := []string{ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", } @@ -322,13 +317,11 @@ image_copy_tmp_dir="storage"` gomega.Expect(defaultConfig.Containers.Env.Get()).To(gomega.BeEquivalentTo(envs)) gomega.Expect(defaultConfig.Containers.Mounts.Get()).To(gomega.BeEquivalentTo(mounts)) gomega.Expect(defaultConfig.Containers.PidsLimit).To(gomega.BeEquivalentTo(2048)) - gomega.Expect(defaultConfig.Network.CNIPluginDirs.Get()).To(gomega.Equal(pluginDirs)) gomega.Expect(defaultConfig.Network.NetavarkPluginDirs.Get()).To(gomega.Equal([]string{"/usr/netavark"})) gomega.Expect(defaultConfig.Engine.NumLocks).To(gomega.BeEquivalentTo(2048)) gomega.Expect(defaultConfig.Engine.OCIRuntimes).To(gomega.Equal(OCIRuntimeMap)) gomega.Expect(defaultConfig.Engine.PlatformToOCIRuntime).To(gomega.Equal(PlatformToOCIRuntimeMap)) gomega.Expect(defaultConfig.Containers.HTTPProxy).To(gomega.BeFalse()) - gomega.Expect(defaultConfig.Engine.NetworkCmdOptions.Get()).To(gomega.BeEmpty()) gomega.Expect(defaultConfig.Engine.HelperBinariesDir.Get()).To(gomega.Equal(helperDirs)) gomega.Expect(defaultConfig.Engine.ServiceTimeout).To(gomega.BeEquivalentTo(300)) gomega.Expect(defaultConfig.Engine.InfraImage).To(gomega.BeEquivalentTo("registry.k8s.io/pause:3.4.1")) @@ -453,7 +446,6 @@ image_copy_tmp_dir="storage"` gomega.Expect(config.Containers.PidsLimit).To(gomega.BeEquivalentTo(2048)) gomega.Expect(config.Containers.Env.Get()).To(gomega.BeEquivalentTo(envs)) gomega.Expect(config.Containers.UserNS).To(gomega.BeEquivalentTo("")) - gomega.Expect(config.Network.CNIPluginDirs.Get()).To(gomega.Equal(DefaultCNIPluginDirs)) gomega.Expect(config.Network.NetavarkPluginDirs.Get()).To(gomega.Equal(DefaultNetavarkPluginDirs)) gomega.Expect(config.Engine.NumLocks).To(gomega.BeEquivalentTo(2048)) gomega.Expect(config.Engine.OCIRuntimes["runc"]).To(gomega.Equal(OCIRuntimeMap["runc"])) diff --git a/common/pkg/config/containers.conf b/common/pkg/config/containers.conf index 2e392d048e..74cb6f7a02 100644 --- a/common/pkg/config/containers.conf +++ b/common/pkg/config/containers.conf @@ -216,12 +216,12 @@ default_sysctls = [ # #log_driver = "k8s-file" -# Default path for container logs to be stored in. When empty, logs will be stored +# Default path for container logs to be stored in. When empty, logs will be stored # in the container's default storage and removed when the container is removed. -# A subdirectory named with the container ID will be created under the specified +# A subdirectory named with the container ID will be created under the specified # path, and the log file will have the default name `ctr.log` within that directory. # This option can be overridden by the `--log-opt` flag. -# +# #log_path = "" # Maximum size allowed for the container log file. Negative numbers indicate @@ -354,25 +354,14 @@ default_sysctls = [ [network] # Network backend determines what network driver will be used to set up and tear down container networks. -# Valid values are "cni" and "netavark". -# The default value is empty which means that it will automatically choose CNI or netavark. If there are -# already containers/images or CNI networks preset it will choose CNI. +# Valid values are "netavark". +# The default value is empty which means that it will automatically choose netavark. # # Before changing this value all containers must be stopped otherwise it is likely that # iptables rules and network interfaces might leak on the host. A reboot will fix this. # #network_backend = "" -# Path to directory where CNI plugin binaries are located. -# -#cni_plugin_dirs = [ -# "/usr/local/libexec/cni", -# "/usr/libexec/cni", -# "/usr/local/lib/cni", -# "/usr/lib/cni", -# "/opt/cni/bin", -#] - # List of directories that will be searched for netavark plugins. # #netavark_plugin_dirs = [ @@ -420,19 +409,11 @@ default_sysctls = [ -# Configure which rootless network program to use by default. Valid options are -# `slirp4netns` and `pasta` (default). +# Configure which rootless network program to use by default. Only current valid option is +# `pasta` (default). # #default_rootless_network_cmd = "pasta" -# Path to the directory where network configuration files are located. -# For the CNI backend the default is "/etc/cni/net.d" as root -# and "$HOME/.config/cni/net.d" as rootless. -# For the netavark backend "/etc/containers/networks" is used as root -# and "$graphroot/networks" as rootless. -# -#network_config_dir = "/etc/cni/net.d/" - # Port to use for dns forwarding daemon with netavark in rootful bridge # mode and dns enabled. # Using an alternate port might be useful if other dns services should @@ -666,32 +647,6 @@ default_sysctls = [ # #namespace = "" -# Path to the slirp4netns binary -# -#network_cmd_path = "" - -# Default options to pass to the slirp4netns binary. -# Valid options values are: -# -# - allow_host_loopback=true|false: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). -# Default is false. -# - mtu=MTU: Specify the MTU to use for this network. (Default is `65520`). -# - cidr=CIDR: Specify ip range to use for this network. (Default is `10.0.2.0/24`). -# - enable_ipv6=true|false: Enable IPv6. Default is true. (Required for `outbound_addr6`). -# - outbound_addr=INTERFACE: Specify the outbound interface slirp should bind to (ipv4 traffic only). -# - outbound_addr=IPv4: Specify the outbound ipv4 address slirp should bind to. -# - outbound_addr6=INTERFACE: Specify the outbound interface slirp should bind to (ipv6 traffic only). -# - outbound_addr6=IPv6: Specify the outbound ipv6 address slirp should bind to. -# - port_handler=rootlesskit: Use rootlesskit for port forwarding. Default. -# Note: Rootlesskit changes the source IP address of incoming packets to a IP address in the container -# network namespace, usually `10.0.2.100`. If your application requires the real source IP address, -# e.g. web server logs, use the slirp4netns port handler. The rootlesskit port handler is also used for -# rootless containers when connected to user-defined networks. -# - port_handler=slirp4netns: Use the slirp4netns port forwarding, it is slower than rootlesskit but -# preserves the correct source IP address. This port handler cannot be used for user-defined networks. -# -#network_cmd_options = [] - # Whether to use chroot instead of pivot_root in the runtime # #no_pivot_root = false diff --git a/common/pkg/config/containers.conf-freebsd b/common/pkg/config/containers.conf-freebsd index bd999c339c..9993b719af 100644 --- a/common/pkg/config/containers.conf-freebsd +++ b/common/pkg/config/containers.conf-freebsd @@ -169,12 +169,12 @@ default_sysctls = [ # #log_driver = "k8s-file" -# Default path for container logs to be stored in. When empty, logs will be stored +# Default path for container logs to be stored in. When empty, logs will be stored # in the container's default storage and removed when the container is removed. -# A subdirectory named with the container ID will be created under the specified +# A subdirectory named with the container ID will be created under the specified # path, and the log file will have the default name `ctr.log` within that directory. # This option can be overridden by the `--log-opt` flag. -# +# #log_path = "" # Maximum size allowed for the container log file. Negative numbers indicate @@ -271,25 +271,14 @@ default_sysctls = [ [network] # Network backend determines what network driver will be used to set up and tear down container networks. -# Valid values are "cni" and "netavark". -# The default value is empty which means that it will automatically choose CNI or netavark. If there are -# already containers/images or CNI networks preset it will choose CNI. +# Valid values are "netavark". +# The default value is empty which means that it will automatically choose netavark. # # Before changing this value all containers must be stopped otherwise it is likely that # iptables rules and network interfaces might leak on the host. A reboot will fix this. # #network_backend = "" -# Path to directory where CNI plugin binaries are located. -# -#cni_plugin_dirs = [ -# "/usr/local/libexec/cni", -# "/usr/libexec/cni", -# "/usr/local/lib/cni", -# "/usr/lib/cni", -# "/opt/cni/bin", -#] - # List of directories that will be searched for netavark plugins. # #netavark_plugin_dirs = [ @@ -327,14 +316,6 @@ default_sysctls = [ # {"base" = "10.128.0.0/9", "size" = 24}, #] -# Path to the directory where network configuration files are located. -# For the CNI backend the default is "/etc/cni/net.d" as root -# and "$HOME/.config/cni/net.d" as rootless. -# For the netavark backend "/etc/containers/networks" is used as root -# and "$graphroot/networks" as rootless. -# -#network_config_dir = "/usr/local/etc/cni/net.d/" - [engine] # Index to the active service # @@ -499,32 +480,6 @@ default_sysctls = [ # #namespace = "" -# Path to the slirp4netns binary -# -#network_cmd_path = "" - -# Default options to pass to the slirp4netns binary. -# Valid options values are: -# -# - allow_host_loopback=true|false: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). -# Default is false. -# - mtu=MTU: Specify the MTU to use for this network. (Default is `65520`). -# - cidr=CIDR: Specify ip range to use for this network. (Default is `10.0.2.0/24`). -# - enable_ipv6=true|false: Enable IPv6. Default is true. (Required for `outbound_addr6`). -# - outbound_addr=INTERFACE: Specify the outbound interface slirp should bind to (ipv4 traffic only). -# - outbound_addr=IPv4: Specify the outbound ipv4 address slirp should bind to. -# - outbound_addr6=INTERFACE: Specify the outbound interface slirp should bind to (ipv6 traffic only). -# - outbound_addr6=IPv6: Specify the outbound ipv6 address slirp should bind to. -# - port_handler=rootlesskit: Use rootlesskit for port forwarding. Default. -# Note: Rootlesskit changes the source IP address of incoming packets to a IP address in the container -# network namespace, usually `10.0.2.100`. If your application requires the real source IP address, -# e.g. web server logs, use the slirp4netns port handler. The rootlesskit port handler is also used for -# rootless containers when connected to user-defined networks. -# - port_handler=slirp4netns: Use the slirp4netns port forwarding, it is slower than rootlesskit but -# preserves the correct source IP address. This port handler cannot be used for user-defined networks. -# -#network_cmd_options = [] - # Whether to use chroot instead of pivot_root in the runtime # #no_pivot_root = false diff --git a/common/pkg/config/default.go b/common/pkg/config/default.go index 0a696517c8..0e3f77e098 100644 --- a/common/pkg/config/default.go +++ b/common/pkg/config/default.go @@ -117,14 +117,6 @@ var ( "CAP_SYS_CHROOT", } - // Search these locations in which CNIPlugins can be installed. - DefaultCNIPluginDirs = []string{ - "/usr/local/libexec/cni", - "/usr/libexec/cni", - "/usr/local/lib/cni", - "/usr/lib/cni", - "/opt/cni/bin", - } DefaultNetavarkPluginDirs = []string{ "/usr/local/libexec/netavark", "/usr/libexec/netavark", @@ -271,7 +263,6 @@ func defaultConfig() (*Config, error) { DefaultSubnetPools: DefaultSubnetPools, DefaultRootlessNetworkCmd: "pasta", DNSBindPort: 0, - CNIPluginDirs: attributedstring.NewSlice(DefaultCNIPluginDirs), NetavarkPluginDirs: attributedstring.NewSlice(DefaultNetavarkPluginDirs), }, Engine: *defaultEngineConfig, diff --git a/common/pkg/config/testdata/containers_comment.conf b/common/pkg/config/testdata/containers_comment.conf index e808bacad1..91df8bd186 100644 --- a/common/pkg/config/testdata/containers_comment.conf +++ b/common/pkg/config/testdata/containers_comment.conf @@ -96,18 +96,6 @@ # Pattern of interface name inside container. # interface_name = "" - -# The network table containers settings pertaining to the management of -# CNI plugins. -[network] - -# Path to the directory where CNI configuration files are located. -# network_config_dir = "/etc/cni/net.d/" - -# Path to directory where CNI plugin binaries are located. -# cni_plugin_dirs = "/usr/libexec/cni" - - [engine] # Cgroup management implementation used for the runtime. diff --git a/common/pkg/config/testdata/containers_default.conf b/common/pkg/config/testdata/containers_default.conf index 28cfb55e53..2444231b6f 100644 --- a/common/pkg/config/testdata/containers_default.conf +++ b/common/pkg/config/testdata/containers_default.conf @@ -111,23 +111,12 @@ umask="0002" # default network mode netns="bridge" -# The network table containers settings pertaining to the management of -# CNI plugins. +# The network table contains settings pertaining to the management of +# netavark and pasta. [network] -# Path to directory where CNI plugin binaries are located. -cni_plugin_dirs = [ - "/usr/libexec/cni", - "/tmp", -] - -# Path to the directory where CNI configuration files are located. -network_config_dir = "/etc/cni/net.d/" - default_subnet_pools = [{"base" = "10.89.0.0/16", "size" = 24}, {"base" = "10.90.0.0/15", "size" = 24}] -default_rootless_network_cmd = "slirp4netns" - # firewall driver to be used by default firewall_driver = "none" diff --git a/common/pkg/config/testdata/containers_invalid.conf b/common/pkg/config/testdata/containers_invalid.conf index ae2600e7e7..da92b3f467 100644 --- a/common/pkg/config/testdata/containers_invalid.conf +++ b/common/pkg/config/testdata/containers_invalid.conf @@ -83,16 +83,6 @@ shm_size = "-5536k" init = false -# The network table containers settings pertaining to the management of -# CNI plugins. -[network] - -# Path to directory where CNI plugin binaries are located. -cni_plugin_dirs = ["/usr/libexec/cni"] - -# Path to the directory where CNI configuration files are located. -network_config_dir = "/etc/cni/net.d/" - [engine] # Cgroup management implementation used for the runtime. diff --git a/common/pkg/rootlessport/rootlessport_linux.go b/common/pkg/rootlessport/rootlessport_linux.go index 78829b9fb6..c333c365ba 100644 --- a/common/pkg/rootlessport/rootlessport_linux.go +++ b/common/pkg/rootlessport/rootlessport_linux.go @@ -22,5 +22,4 @@ type Config struct { TmpDir string ChildIP string ContainerID string - RootlessCNI bool } diff --git a/common/pkg/ssh/types.go b/common/pkg/ssh/types.go index 3f59fee8c8..071579d006 100644 --- a/common/pkg/ssh/types.go +++ b/common/pkg/ssh/types.go @@ -126,7 +126,6 @@ type HostInfo struct { // ServiceIsRemote is true when the podman/libpod service is remote to the client ServiceIsRemote bool `json:"serviceIsRemote"` Security SecurityInfo `json:"security"` - Slirp4NetNS SlirpInfo `json:"slirp4netns,omitempty"` SwapFree int64 `json:"swapFree"` SwapTotal int64 `json:"swapTotal"` Uptime string `json:"uptime"` @@ -139,13 +138,6 @@ type RemoteSocket struct { Exists bool `json:"exists,omitempty"` } -// SlirpInfo describes the slirp executable that is being used. -type SlirpInfo struct { - Executable string `json:"executable"` - Package string `json:"package"` - Version string `json:"version"` -} - // IDMappings describe the GID and UID mappings. type IDMappings struct { GIDMap []idtools.IDMap `json:"gidmap"` diff --git a/common/pkg/systemd/systemd_linux.go b/common/pkg/systemd/systemd_linux.go index 1d839636aa..a98632a130 100644 --- a/common/pkg/systemd/systemd_linux.go +++ b/common/pkg/systemd/systemd_linux.go @@ -58,9 +58,9 @@ func moveProcessToScope(pid int, slice, scope string) error { return err } -// MoveRootlessNetnsSlirpProcessToUserSlice moves the slirp4netns process for the rootless netns +// MoveRootlessNetnsProcessToUserSlice moves the process for the rootless netns // into a different scope so that systemd does not kill it with a container. -func MoveRootlessNetnsSlirpProcessToUserSlice(pid int) error { +func MoveRootlessNetnsProcessToUserSlice(pid int) error { randBytes := make([]byte, 4) _, err := rand.Read(randBytes) if err != nil { diff --git a/common/rpm/containers-common.spec b/common/rpm/containers-common.spec index d478ab7dee..8648f07d79 100644 --- a/common/rpm/containers-common.spec +++ b/common/rpm/containers-common.spec @@ -70,7 +70,6 @@ Recommends: composefs Recommends: crun Requires: (crun if fedora-release-identity-server) Requires: netavark >= %{netavark_epoch}:1.10.3-1 -Suggests: slirp4netns Recommends: qemu-user-static Requires: (qemu-user-static-aarch64 if fedora-release-identity-server) Requires: (qemu-user-static-arm if fedora-release-identity-server)