diff --git a/common/.golangci.yml b/common/.golangci.yml index ccbc901ce9..db2be8ee64 100644 --- a/common/.golangci.yml +++ b/common/.golangci.yml @@ -9,7 +9,6 @@ run: - systemd - exclude_graphdriver_btrfs - containers_image_openpgp - - cni formatters: enable: diff --git a/common/Makefile b/common/Makefile index c32772e858..356f022777 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) @@ -106,7 +102,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 dccc6b93c9..73692c48ca 100644 --- a/common/docs/containers.conf.5.md +++ b/common/docs/containers.conf.5.md @@ -418,33 +418,13 @@ 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 -plugins. +The `network` table contains settings pertaining to the management of container +networks. **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. - -Before changing this value all containers must be stopped otherwise it is likely that -firewall 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", -] -``` +The only supported value is "netavark". An empty string defaults to "netavark". **netavark_plugin_dirs**=[] @@ -498,12 +478,10 @@ default_subnet_pools = [ Configure which rootless network program to use by default. Valid options are `slirp4netns` and `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 +The default is "/etc/containers/networks" as root and "$graphroot/networks" as rootless. **firewall_driver**="" diff --git a/common/go.mod b/common/go.mod index bf08d03e3b..757615ee2c 100644 --- a/common/go.mod +++ b/common/go.mod @@ -9,8 +9,6 @@ require ( github.com/checkpoint-restore/checkpointctl v1.5.0 github.com/checkpoint-restore/go-criu/v8 v8.2.0 github.com/containerd/platforms v1.0.0-rc.2 - github.com/containernetworking/cni v1.3.0 - github.com/containernetworking/plugins v1.9.0 github.com/containers/ocicrypt v1.2.1 github.com/coreos/go-systemd/v22 v22.7.0 github.com/cyphar/filepath-securejoin v0.6.1 diff --git a/common/go.sum b/common/go.sum index 95b81dc64d..21d2a7a0d4 100644 --- a/common/go.sum +++ b/common/go.sum @@ -47,10 +47,6 @@ github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6a github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw= github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY= -github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEmnuFjskwo= -github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4= -github.com/containernetworking/plugins v1.9.0 h1:Mg3SXBdRGkdXyFC4lcwr6u2ZB2SDeL6LC3U+QrEANuQ= -github.com/containernetworking/plugins v1.9.0/go.mod h1:JG3BxoJifxxHBhG3hFyxyhid7JgRVBu/wtooGEvWf1c= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.2.1 h1:0qIOTT9DoYwcKmxSt8QJt+VzMY18onl9jUXsxpVhSmM= 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 33220a2827..0000000000 --- a/common/libnetwork/cni/network.go +++ /dev/null @@ -1,359 +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, - DefaultNetwork: n.defaultNetwork, - } - - 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 0494081b88..0000000000 --- a/common/libnetwork/cni/run_test.go +++ /dev/null @@ -1,1407 +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 - // 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 - 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 - // 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 - wg.Go(func() { - defer GinkgoRecover() - 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()) - wg.Go(func() { - defer GinkgoRecover() - 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 deleted file mode 100644 index edc29f66fe..0000000000 --- a/common/libnetwork/internal/rootlessnetns/netns.go +++ /dev/null @@ -1,8 +0,0 @@ -package rootlessnetns - -type NetworkBackend int - -const ( - Netavark NetworkBackend = iota - CNI -) diff --git a/common/libnetwork/internal/rootlessnetns/netns_freebsd.go b/common/libnetwork/internal/rootlessnetns/netns_freebsd.go index 03322dc52b..f1ae4bb00c 100644 --- a/common/libnetwork/internal/rootlessnetns/netns_freebsd.go +++ b/common/libnetwork/internal/rootlessnetns/netns_freebsd.go @@ -12,7 +12,7 @@ var ErrNotSupported = errors.New("rootless netns only supported on linux") type Netns struct{} -func New(dir string, backend NetworkBackend, conf *config.Config) (*Netns, error) { +func New(dir string, conf *config.Config) (*Netns, error) { return nil, ErrNotSupported } diff --git a/common/libnetwork/internal/rootlessnetns/netns_linux.go b/common/libnetwork/internal/rootlessnetns/netns_linux.go index 05b3b16dd6..049df8dc35 100644 --- a/common/libnetwork/internal/rootlessnetns/netns_linux.go +++ b/common/libnetwork/internal/rootlessnetns/netns_linux.go @@ -11,7 +11,6 @@ import ( "strconv" "strings" - "github.com/containernetworking/plugins/pkg/ns" "github.com/hashicorp/go-multierror" "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux/label" @@ -41,9 +40,6 @@ const ( // rootlessNetNsConnPidFile is the name of the rootless netns slirp4netns/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" @@ -52,8 +48,6 @@ const ( type Netns struct { // dir used for the rootless netns dir string - // backend used for the network setup/teardown - backend NetworkBackend // config contains containers.conf options. config *config.Config @@ -88,15 +82,14 @@ func wrapError(msg string, err error) *rootlessNetnsError { } } -func New(dir string, backend NetworkBackend, conf *config.Config) (*Netns, error) { +func New(dir string, conf *config.Config) (*Netns, error) { netnsDir := filepath.Join(dir, rootlessNetnsDir) if err := os.MkdirAll(netnsDir, 0o700); err != nil { return nil, wrapError("", err) } return &Netns{ - dir: netnsDir, - backend: backend, - config: conf, + dir: netnsDir, + config: conf, }, nil } @@ -107,9 +100,9 @@ func (n *Netns) getPath(path string) string { // getOrCreateNetns returns the rootless netns, if it created a new one the // returned bool is set to true. -func (n *Netns) getOrCreateNetns() (ns.NetNS, bool, error) { +func (n *Netns) getOrCreateNetns() (netns.NetNS, bool, error) { nsPath := n.getPath(rootlessNetnsDir) - nsRef, err := ns.GetNS(nsPath) + nsRef, err := netns.GetNS(nsPath) if err == nil { pidPath := n.getPath(rootlessNetNsConnPidFile) pid, err := readPidFile(pidPath) @@ -140,7 +133,7 @@ func (n *Netns) getOrCreateNetns() (ns.NetNS, bool, error) { // the file and mounting it. Or if the file is not on tmpfs (deleted on boot) // you might run into it as well: https://github.com/containers/podman/issues/25144 // We have to do this because NewNSAtPath fails with EEXIST otherwise - if errors.As(err, &ns.NSPathNotNSErr{}) { + if errors.As(err, &netns.NSPathNotNSErr{}) { // We don't care if this fails, NewNSAtPath() should return the real error. _ = os.Remove(nsPath) } @@ -349,16 +342,15 @@ func (n *Netns) setupMounts() error { // we have to set up all mounts correctly. // The order of the mounts is IMPORTANT. - // The idea of the extra mount ns is to make /run and /var/lib/cni writeable - // for the cni plugins but not affecting the podman user namespace. + // The idea of the extra mount ns is to make /run writeable + // for the network plugins but not affecting the podman user namespace. // 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 + // 4. XDG_RUNTIME_DIR/rootless-netns/run -> /run // Create a new mount namespace, // this must happen inside the netns thread. @@ -385,7 +377,7 @@ func (n *Netns) setupMounts() error { } 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. + // Otherwise network 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 @@ -501,14 +493,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. + // 4. 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 +515,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 { @@ -577,7 +532,7 @@ func (n *Netns) runInner(toRun func() error, cleanup bool) (err error) { }() } - return nsRef.Do(func(_ ns.NetNS) error { + return nsRef.Do(func(_ netns.NetNS) error { if err := n.setupMounts(); err != nil { return err } diff --git a/common/libnetwork/netavark/config.go b/common/libnetwork/netavark/config.go index 7dcbe7bb3c..f8631d2895 100644 --- a/common/libnetwork/netavark/config.go +++ b/common/libnetwork/netavark/config.go @@ -153,7 +153,7 @@ func (n *netavarkNetwork) networkCreate(newNetwork *types.Network, defaultNet bo // 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 + // This could happen if you run a container on this net, then the network 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. diff --git a/common/libnetwork/netavark/config_test.go b/common/libnetwork/netavark/config_test.go index 4ae61da9ea..cfb143836e 100644 --- a/common/libnetwork/netavark/config_test.go +++ b/common/libnetwork/netavark/config_test.go @@ -38,7 +38,7 @@ var _ = Describe("Config", func() { var err error libpodNet, err = getNetworkInterface(networkConfDir) if err != nil { - Fail("Failed to create NewCNINetworkInterface") + Fail("Failed to create NewNetworkInterface") } }) diff --git a/common/libnetwork/netavark/ipam_test.go b/common/libnetwork/netavark/ipam_test.go index f12534235b..459c7f385f 100644 --- a/common/libnetwork/netavark/ipam_test.go +++ b/common/libnetwork/netavark/ipam_test.go @@ -35,7 +35,7 @@ var _ = Describe("IPAM", func() { NetworkRunDir: networkConfDir, }) if err != nil { - Fail("Failed to create NewCNINetworkInterface") + Fail("Failed to create NewNetworkInterface") } networkInterface = libpodNet.(*netavarkNetwork) //nolint:errcheck // It is always *netavarkNetwork here. diff --git a/common/libnetwork/netavark/network.go b/common/libnetwork/netavark/network.go index 9dbe3b452d..3a35b2af82 100644 --- a/common/libnetwork/netavark/network.go +++ b/common/libnetwork/netavark/network.go @@ -104,7 +104,7 @@ func NewNetworkInterface(conf *InitConfig) (types.ContainerNetwork, error) { val, ok := os.LookupEnv(unshare.UsernsEnvName) useRootlessNetns := ok && val == "done" if useRootlessNetns { - netns, err = rootlessnetns.New(conf.NetworkRunDir, rootlessnetns.Netavark, conf.Config) + netns, err = rootlessnetns.New(conf.NetworkRunDir, conf.Config) if err != nil { return nil, err } @@ -354,7 +354,7 @@ func (n *netavarkNetwork) Len() int { return len(n.networks) } -// DefaultInterfaceName return the default cni bridge name, must be suffixed with a number. +// DefaultInterfaceName return the default bridge name, must be suffixed with a number. func (n *netavarkNetwork) DefaultInterfaceName() string { return defaultBridgeName } diff --git a/common/libnetwork/netavark/run_test.go b/common/libnetwork/netavark/run_test.go index b6d7f32747..416c4ba7d0 100644 --- a/common/libnetwork/netavark/run_test.go +++ b/common/libnetwork/netavark/run_test.go @@ -21,7 +21,6 @@ import ( "sync" "time" - "github.com/containernetworking/plugins/pkg/ns" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/sirupsen/logrus" @@ -37,14 +36,14 @@ var _ = Describe("run netavark", func() { var ( libpodNet types.ContainerNetwork confDir string - netNSTest ns.NetNS - netNSContainer ns.NetNS + netNSTest netns.NetNS + netNSContainer netns.NetNS ) // 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. + // is run in its own netns. runTest := func(run func()) { - _ = netNSTest.Do(func(_ ns.NetNS) error { + _ = netNSTest.Do(func(_ netns.NetNS) error { defer GinkgoRecover() // we have to setup the loopback adapter in this netns to use port forwarding link, err := netlink.LinkByName("lo") @@ -63,7 +62,7 @@ var _ = Describe("run netavark", 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. + // the rootless netns code but this is too much work to get it right for a unit test. if unshare.IsRootless() { Skip("this test needs to be run as root") } @@ -108,7 +107,7 @@ var _ = Describe("run netavark", func() { var err error libpodNet, err = getNetworkInterface(confDir) if err != nil { - Fail("Failed to create NewCNINetworkInterface") + Fail("Failed to create NewNetworkInterface") } }) @@ -152,7 +151,7 @@ var _ = Describe("run netavark", func() { Expect(res[defNet].DNSSearchDomains).To(BeEmpty()) // check in the container namespace if the settings are applied - err = netNSContainer.Do(func(_ ns.NetNS) error { + err = netNSContainer.Do(func(_ netns.NetNS) error { defer GinkgoRecover() i, err := net.InterfaceByName(intName) Expect(err).ToNot(HaveOccurred()) @@ -194,7 +193,7 @@ var _ = Describe("run netavark", func() { wg := &sync.WaitGroup{} expected := stringid.GenerateNonCryptoID() // now check ip connectivity - err = netNSContainer.Do(func(_ ns.NetNS) error { + err = netNSContainer.Do(func(_ netns.NetNS) error { runNetListener(wg, "tcp", "0.0.0.0", 5000, expected) return nil }) @@ -240,7 +239,7 @@ var _ = Describe("run netavark", func() { Expect(macAddress).To(Equal(mac)) // check in the container namespace if the settings are applied - err = netNSContainer.Do(func(_ ns.NetNS) error { + err = netNSContainer.Do(func(_ netns.NetNS) error { defer GinkgoRecover() i, err := net.InterfaceByName(intName) Expect(err).ToNot(HaveOccurred()) @@ -348,7 +347,7 @@ var _ = Describe("run netavark", func() { Expect(res[netName].Interfaces[intName].MacAddress).To(HaveLen(6)) // check in the container namespace if the settings are applied - err = netNSContainer.Do(func(_ ns.NetNS) error { + err = netNSContainer.Do(func(_ netns.NetNS) error { defer GinkgoRecover() i, err := net.InterfaceByName(intName) Expect(err).ToNot(HaveOccurred()) @@ -451,7 +450,7 @@ var _ = Describe("run netavark", func() { Expect(mac2).To(HaveLen(6)) // check in the container namespace if the settings are applied - err = netNSContainer.Do(func(_ ns.NetNS) error { + err = netNSContainer.Do(func(_ netns.NetNS) error { defer GinkgoRecover() i, err := net.InterfaceByName(intName1) Expect(err).ToNot(HaveOccurred()) @@ -545,7 +544,7 @@ var _ = Describe("run netavark", func() { Expect(res[defNet].DNSSearchDomains).To(BeEmpty()) var wg sync.WaitGroup // start a listener in the container ns - err = netNSContainer.Do(func(_ ns.NetNS) error { + err = netNSContainer.Do(func(_ netns.NetNS) error { defer GinkgoRecover() runNetListener(&wg, protocol, "0.0.0.0", 5000, testdata) return nil @@ -604,7 +603,7 @@ var _ = Describe("run netavark", func() { var wg sync.WaitGroup testdata := stringid.GenerateNonCryptoID() // start a listener in the container ns - err = netNSContainer.Do(func(_ ns.NetNS) error { + err = netNSContainer.Do(func(_ netns.NetNS) error { defer GinkgoRecover() runNetListener(&wg, protocol, containerIP, port-1, testdata) return nil @@ -657,7 +656,7 @@ var _ = Describe("run netavark", func() { err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(opts)) Expect(err).ToNot(HaveOccurred()) - err = netNSContainer.Do(func(_ ns.NetNS) error { + err = netNSContainer.Do(func(_ netns.NetNS) error { defer GinkgoRecover() // check that the container interface is removed _, err := net.InterfaceByName(intName) @@ -677,7 +676,7 @@ var _ = Describe("run netavark", func() { It("test netavark error", func() { runTest(func() { intName := "eth0" - err := netNSContainer.Do(func(_ ns.NetNS) error { + err := netNSContainer.Do(func(_ netns.NetNS) error { defer GinkgoRecover() attr := netlink.NewLinkAttrs() @@ -741,7 +740,7 @@ var _ = Describe("run netavark", func() { Expect(macInt1).To(HaveLen(6)) // check in the container namespace if the settings are applied - err = netNSContainer.Do(func(_ ns.NetNS) error { + err = netNSContainer.Do(func(_ netns.NetNS) error { defer GinkgoRecover() i, err := net.InterfaceByName(intName1) Expect(err).ToNot(HaveOccurred()) @@ -770,7 +769,7 @@ var _ = Describe("run netavark", func() { Expect(err).ToNot(HaveOccurred()) // check in the container namespace that the interface is removed - err = netNSContainer.Do(func(_ ns.NetNS) error { + err = netNSContainer.Do(func(_ netns.NetNS) error { defer GinkgoRecover() _, err := net.InterfaceByName(intName1) Expect(err).To(HaveOccurred()) diff --git a/common/libnetwork/network/interface.go b/common/libnetwork/network/interface.go index 82955b6edf..4a08e85339 100644 --- a/common/libnetwork/network/interface.go +++ b/common/libnetwork/network/interface.go @@ -3,51 +3,41 @@ package network import ( - "errors" "fmt" - "os" "path/filepath" - "github.com/sirupsen/logrus" "go.podman.io/common/libnetwork/netavark" "go.podman.io/common/libnetwork/types" "go.podman.io/common/pkg/config" "go.podman.io/storage" - "go.podman.io/storage/pkg/ioutils" "go.podman.io/storage/pkg/unshare" ) const ( - // defaultNetworkBackendFileName is the file name for sentinel file to store the backend. - defaultNetworkBackendFileName = "defaultNetworkBackend" - // netavarkBinary is the name of the netavark binary. netavarkBinary = "netavark" // aardvarkBinary is the name of the aardvark binary. aardvarkBinary = "aardvark-dns" ) -// 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 +// NetworkBackend returns the network backend name and interface. +// It returns the netavark backend. If no backend is set in the config, +// it defaults to netavark. Any other backend value is an error. // // revive does not like the name because the package is already called network // //nolint:revive func NetworkBackend(store storage.Store, conf *config.Config, syslog bool) (types.NetworkBackend, types.ContainerNetwork, error) { backend := types.NetworkBackend(conf.Network.NetworkBackend) - if backend == "" { - var err error - backend, err = defaultNetworkBackend(store, conf) - if err != nil { - return "", nil, fmt.Errorf("failed to get default network backend: %w", err) - } + if backend != "" && backend != types.Netavark { + return "", nil, fmt.Errorf("unsupported network backend %q, only netavark is supported", backend) } - return backendFromType(backend, store, conf, syslog) + netInt, err := netavarkBackendFromConf(store, conf, syslog) + if err != nil { + return "", nil, err + } + return types.Netavark, netInt, nil } func netavarkBackendFromConf(store storage.Store, conf *config.Config, syslog bool) (types.ContainerNetwork, error) { @@ -83,60 +73,6 @@ 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) { - err = nil - - file := filepath.Join(store.GraphRoot(), defaultNetworkBackendFileName) - - writeBackendToFile := func(backendT types.NetworkBackend) { - // only write when there is no error - if err == nil { - if err := ioutils.AtomicWriteFile(file, []byte(backendT), 0o644); err != nil { - logrus.Errorf("could not write network backend to file: %v", err) - } - } - } - - // read defaultNetworkBackend file - b, err := os.ReadFile(file) - 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) { - // 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) - } - - // fail for all errors except ENOENT - if !errors.Is(err, os.ErrNotExist) { - 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 -} - // getDefaultNetavarkConfigDir return the netavark config dir. For rootful it will // use "/etc/containers/networks" and for rootless "$graphroot/networks". We cannot // use the graphroot for rootful since the network namespace is shared for all 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/pasta/pasta_linux.go b/common/libnetwork/pasta/pasta_linux.go index a72e7adfde..91bbb16534 100644 --- a/common/libnetwork/pasta/pasta_linux.go +++ b/common/libnetwork/pasta/pasta_linux.go @@ -18,11 +18,11 @@ import ( "slices" "strings" - "github.com/containernetworking/plugins/pkg/ns" "github.com/sirupsen/logrus" "go.podman.io/common/libnetwork/types" "go.podman.io/common/libnetwork/util" "go.podman.io/common/pkg/config" + "go.podman.io/common/pkg/netns" ) const ( @@ -104,7 +104,7 @@ func Setup(opts *SetupOptions) (*SetupResult, error) { var ipv4, ipv6 bool result := &SetupResult{} - err = ns.WithNetNSPath(opts.Netns, func(_ ns.NetNS) error { + err = netns.WithNetNSPath(opts.Netns, func(_ netns.NetNS) error { addrs, err := net.InterfaceAddrs() if err != nil { return err diff --git a/common/libnetwork/slirp4netns/slirp4netns.go b/common/libnetwork/slirp4netns/slirp4netns.go index 397f55f505..74126c608c 100644 --- a/common/libnetwork/slirp4netns/slirp4netns.go +++ b/common/libnetwork/slirp4netns/slirp4netns.go @@ -18,10 +18,10 @@ import ( "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/netns" "go.podman.io/common/pkg/rootlessport" "go.podman.io/common/pkg/servicereaper" "go.podman.io/common/pkg/util" @@ -334,7 +334,7 @@ func Setup(opts *SetupOptions) (*SetupResult, error) { netnsReadyWg.Add(1) go func() { - err := ns.WithNetNSPath(opts.Netns, func(_ ns.NetNS) error { + err := netns.WithNetNSPath(opts.Netns, func(_ netns.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. diff --git a/common/libnetwork/types/const.go b/common/libnetwork/types/const.go index d8730dcdf0..c027d933b4 100644 --- a/common/libnetwork/types/const.go +++ b/common/libnetwork/types/const.go @@ -19,9 +19,9 @@ const ( // NoneIPAMDriver do not provide ipam management. NoneIPAMDriver = "none" - // DefaultSubnet is the name that will be used for the default CNI network. + // DefaultNetworkName is the name that will be used for the default network. DefaultNetworkName = "podman" - // DefaultSubnet is the subnet that will be used for the default CNI network. + // DefaultSubnet is the subnet that will be used for the default network. DefaultSubnet = "10.88.0.0/16" BridgeModeManaged = "managed" @@ -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 b434e011de..445acdfbfe 100644 --- a/common/pkg/config/config.go +++ b/common/pkg/config/config.go @@ -47,7 +47,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 configuration of network plugins Network NetworkConfig `toml:"network"` // Secret section defines configurations for the secret management Secrets SecretConfig `toml:"secrets"` @@ -590,9 +590,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"` diff --git a/common/pkg/config/config_local_test.go b/common/pkg/config/config_local_test.go index a571a7b7a5..b90b827ade 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{ diff --git a/common/pkg/config/config_remote_test.go b/common/pkg/config/config_remote_test.go index d4897288cb..67092293b1 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() @@ -120,39 +101,4 @@ var _ = Describe("Config Remote", func() { 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 7dd5f38476..cef1f0f42a 100644 --- a/common/pkg/config/config_test.go +++ b/common/pkg/config/config_test.go @@ -272,7 +272,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 c2249c3740..f07c9dbf31 100644 --- a/common/pkg/config/containers.conf +++ b/common/pkg/config/containers.conf @@ -354,25 +354,10 @@ 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. -# -# Before changing this value all containers must be stopped otherwise it is likely that -# firewall rules and network interfaces might leak on the host. A reboot will fix this. +# The only supported value is "netavark". # #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 = [ @@ -426,12 +411,10 @@ default_sysctls = [ #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 +# The default is "/etc/containers/networks" as root # and "$graphroot/networks" as rootless. # -#network_config_dir = "/etc/cni/net.d/" +#network_config_dir = "/etc/containers/networks" # Port to use for dns forwarding daemon with netavark in rootful bridge # mode and dns enabled. diff --git a/common/pkg/config/containers.conf-freebsd b/common/pkg/config/containers.conf-freebsd index d76235a24d..620bd0ca4f 100644 --- a/common/pkg/config/containers.conf-freebsd +++ b/common/pkg/config/containers.conf-freebsd @@ -271,25 +271,10 @@ 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. -# -# 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. +# The only supported value is "netavark". # #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 = [ @@ -328,12 +313,10 @@ default_sysctls = [ #] # 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 +# The default is "/usr/local/etc/containers/networks" as root # and "$graphroot/networks" as rootless. # -#network_config_dir = "/usr/local/etc/cni/net.d/" +#network_config_dir = "/usr/local/etc/containers/networks" # The default host IPs to bind published container ports to when no host IP # is explicitly specified in the -p flag (e.g., -p 8000:8000). If empty, the default diff --git a/common/pkg/config/default.go b/common/pkg/config/default.go index ee4dd63791..f7c425546f 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", @@ -272,7 +264,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..b4a09a0724 100644 --- a/common/pkg/config/testdata/containers_comment.conf +++ b/common/pkg/config/testdata/containers_comment.conf @@ -97,16 +97,12 @@ # interface_name = "" -# The network table containers settings pertaining to the management of -# CNI plugins. +# The network table contains settings pertaining to the management of +# network 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" - +# Path to the directory where network configuration files are located. +# network_config_dir = "/etc/containers/networks" [engine] diff --git a/common/pkg/config/testdata/containers_default.conf b/common/pkg/config/testdata/containers_default.conf index c7d7ae3f64..a9e20c16e0 100644 --- a/common/pkg/config/testdata/containers_default.conf +++ b/common/pkg/config/testdata/containers_default.conf @@ -111,18 +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 +# network plugins. [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/" +# Path to the directory where network configuration files are located. +network_config_dir = "/etc/containers/networks" default_subnet_pools = [{"base" = "10.89.0.0/16", "size" = 24}, {"base" = "10.90.0.0/15", "size" = 24}] diff --git a/common/pkg/config/testdata/containers_invalid.conf b/common/pkg/config/testdata/containers_invalid.conf index ae2600e7e7..cce3891eb2 100644 --- a/common/pkg/config/testdata/containers_invalid.conf +++ b/common/pkg/config/testdata/containers_invalid.conf @@ -83,15 +83,12 @@ shm_size = "-5536k" init = false -# The network table containers settings pertaining to the management of -# CNI plugins. +# The network table contains settings pertaining to the management of +# network 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/" +# Path to the directory where network configuration files are located. +network_config_dir = "/etc/containers/networks" [engine] diff --git a/common/pkg/netns/netns_linux.go b/common/pkg/netns/netns_linux.go index 4283ffd8d7..c2ee879d62 100644 --- a/common/pkg/netns/netns_linux.go +++ b/common/pkg/netns/netns_linux.go @@ -1,4 +1,4 @@ -// Copyright 2018 CNI authors +// Copyright 2015-2018 CNI authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -29,9 +29,9 @@ import ( "runtime" "strings" "sync" + "syscall" "time" - "github.com/containernetworking/plugins/pkg/ns" "github.com/sirupsen/logrus" "go.podman.io/storage/pkg/homedir" "go.podman.io/storage/pkg/unshare" @@ -43,6 +43,209 @@ const threadNsPath = "/proc/thread-self/ns/net" var errNoFreeName = errors.New("failed to find free netns path name") +// NetNS defines a network namespace. +type NetNS interface { + // Do executes the passed closure in this object's network namespace, + // attempting to restore the original namespace before returning. + // However, since each OS thread can have a different network namespace, + // and Go's thread scheduling is highly variable, callers cannot + // guarantee any specific namespace is set unless operations that + // require that namespace are wrapped with Do(). Also, no code called + // from Do() should call runtime.UnlockOSThread(), or the risk + // of executing code in an incorrect namespace will be greater. See + // https://github.com/golang/go/wiki/LockOSThread for further details. + Do(toRun func(NetNS) error) error + + // Set sets the current network namespace to this object's network namespace. + // Note that since Go's thread scheduling is highly variable, callers + // cannot guarantee the requested namespace will be the current namespace + // after this function is called; to ensure this wrap operations that + // require the namespace with Do() instead. + Set() error + + // Path returns the filesystem path representing this object's network namespace. + Path() string + + // Fd returns a file descriptor representing this object's network namespace. + Fd() uintptr + + // Close cleans up this instance of the network namespace; if this instance + // is the last user the namespace will be destroyed. + Close() error +} + +type netNS struct { + file *os.File + closed bool +} + +// netNS implements the NetNS interface. +var _ NetNS = &netNS{} + +const ( + // https://github.com/torvalds/linux/blob/master/include/uapi/linux/magic.h + nsfsMagic = unix.NSFS_MAGIC + procfsMagic = unix.PROC_SUPER_MAGIC +) + +// NSPathNotExistErr is returned when the netns path does not exist. +type NSPathNotExistErr struct{ msg string } + +func (e NSPathNotExistErr) Error() string { return e.msg } + +// NSPathNotNSErr is returned when the path exists but is not a network namespace. +type NSPathNotNSErr struct{ msg string } + +func (e NSPathNotNSErr) Error() string { return e.msg } + +// IsNSorErr checks whether the given path is a network namespace. +func IsNSorErr(nspath string) error { + stat := syscall.Statfs_t{} + if err := syscall.Statfs(nspath, &stat); err != nil { + if os.IsNotExist(err) { + err = NSPathNotExistErr{msg: fmt.Sprintf("failed to Statfs %q: %v", nspath, err)} + } else { + err = fmt.Errorf("failed to Statfs %q: %v", nspath, err) + } + return err + } + + switch stat.Type { + case procfsMagic, nsfsMagic: + return nil + default: + return NSPathNotNSErr{msg: fmt.Sprintf("unknown FS magic on %q: %x", nspath, stat.Type)} + } +} + +// GetNS returns an object representing the namespace referred to by @path. +func GetNS(nspath string) (NetNS, error) { + err := IsNSorErr(nspath) + if err != nil { + return nil, err + } + + fd, err := os.Open(nspath) + if err != nil { + return nil, err + } + + return &netNS{file: fd}, nil +} + +// GetCurrentNS returns an object representing the current OS thread's network namespace. +func GetCurrentNS() (NetNS, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + return GetNS(threadNsPath) +} + +// WithNetNSPath executes the passed closure under the given network +// namespace, restoring the original namespace afterwards. +func WithNetNSPath(nspath string, toRun func(NetNS) error) error { + ns, err := GetNS(nspath) + if err != nil { + return err + } + defer ns.Close() + return ns.Do(toRun) +} + +func (ns *netNS) Path() string { + return ns.file.Name() +} + +func (ns *netNS) Fd() uintptr { + return ns.file.Fd() +} + +func (ns *netNS) errorIfClosed() error { + if ns.closed { + return fmt.Errorf("%q has already been closed", ns.file.Name()) + } + return nil +} + +func (ns *netNS) Close() error { + if err := ns.errorIfClosed(); err != nil { + return err + } + + if err := ns.file.Close(); err != nil { + return fmt.Errorf("failed to close %q: %v", ns.file.Name(), err) + } + ns.closed = true + + return nil +} + +func (ns *netNS) Set() error { + if err := ns.errorIfClosed(); err != nil { + return err + } + + if err := unix.Setns(int(ns.Fd()), unix.CLONE_NEWNET); err != nil { + return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err) + } + + return nil +} + +func (ns *netNS) Do(toRun func(NetNS) error) error { + if err := ns.errorIfClosed(); err != nil { + return err + } + + containedCall := func(hostNS NetNS) error { + threadNS, err := GetNS(threadNsPath) + if err != nil { + return fmt.Errorf("failed to open current netns: %v", err) + } + defer threadNS.Close() + + // switch to target namespace + if err = ns.Set(); err != nil { + return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err) + } + defer func() { + err := threadNS.Set() // switch back + if err == nil { + // Unlock the current thread only when we successfully switched back + // to the original namespace; otherwise leave the thread locked which + // will force the runtime to scrap the current thread, that is maybe + // not as optimal but at least always safe to do. + runtime.UnlockOSThread() + } + }() + + return toRun(hostNS) + } + + // save a handle to current network namespace + hostNS, err := GetCurrentNS() + if err != nil { + return fmt.Errorf("failed to open current namespace: %v", err) + } + defer hostNS.Close() + + var wg sync.WaitGroup + wg.Add(1) + + // Start the callback in a new green thread so that if we later fail + // to switch the namespace back to the original one, we can safely + // leave the thread locked to die without a risk of the current thread + // left lingering with incorrect namespace. + var innerError error + go func() { + defer wg.Done() + runtime.LockOSThread() + innerError = containedCall(hostNS) + }() + wg.Wait() + + return innerError +} + // GetNSRunDir returns the dir of where to create the netNS. When running // rootless, it needs to be at a location writable by user. func GetNSRunDir() (string, error) { @@ -56,13 +259,15 @@ func GetNSRunDir() (string, error) { return "/run/netns", nil } -func NewNSAtPath(nsPath string) (ns.NetNS, error) { +// NewNSAtPath creates a new persistent (bind-mounted) network namespace +// at the given path and returns an object representing that namespace. +func NewNSAtPath(nsPath string) (NetNS, error) { return newNSPath(nsPath) } // NewNS creates a new persistent (bind-mounted) network namespace and returns // an object representing that namespace, without switching to it. -func NewNS() (ns.NetNS, error) { +func NewNS() (NetNS, error) { nsRunDir, err := GetNSRunDir() if err != nil { return nil, err @@ -208,7 +413,7 @@ func createNetnsFile(path string) error { return mountPointFd.Close() } -func newNSPath(nsPath string) (ns.NetNS, error) { +func newNSPath(nsPath string) (NetNS, error) { // create an empty file to use as at the mount point err := createNetnsFile(nsPath) if err != nil { @@ -253,7 +458,7 @@ func newNSPath(nsPath string) (ns.NetNS, error) { return nil, fmt.Errorf("failed to create namespace: %v", err) } - return ns.GetNS(nsPath) + return GetNS(nsPath) } // UnmountNS unmounts the given netns path. diff --git a/vendor/github.com/containernetworking/cni/LICENSE b/vendor/github.com/containernetworking/cni/LICENSE deleted file mode 100644 index 8f71f43fee..0000000000 --- a/vendor/github.com/containernetworking/cni/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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 - - http://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. - diff --git a/vendor/github.com/containernetworking/cni/libcni/api.go b/vendor/github.com/containernetworking/cni/libcni/api.go deleted file mode 100644 index 6ac26949e6..0000000000 --- a/vendor/github.com/containernetworking/cni/libcni/api.go +++ /dev/null @@ -1,900 +0,0 @@ -// Copyright 2015 CNI authors -// -// 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 -// -// http://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. - -package libcni - -// Note this is the actual implementation of the CNI specification, which -// is reflected in the SPEC.md file. -// it is typically bundled into runtime providers (i.e. containerd or cri-o would use this -// before calling runc or hcsshim). It is also bundled into CNI providers as well, for example, -// to add an IP to a container, to parse the configuration of the CNI and so on. - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "os" - "path/filepath" - "sort" - "strings" - - "github.com/containernetworking/cni/pkg/invoke" - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/create" - "github.com/containernetworking/cni/pkg/utils" - "github.com/containernetworking/cni/pkg/version" -) - -var ( - CacheDir = "/var/lib/cni" - // slightly awkward wording to preserve anyone matching on error strings - ErrorCheckNotSupp = fmt.Errorf("does not support the CHECK command") -) - -const ( - CNICacheV1 = "cniCacheV1" -) - -// A RuntimeConf holds the arguments to one invocation of a CNI plugin -// excepting the network configuration, with the nested exception that -// the `runtimeConfig` from the network configuration is included -// here. -type RuntimeConf struct { - ContainerID string - NetNS string - IfName string - Args [][2]string - // A dictionary of capability-specific data passed by the runtime - // to plugins as top-level keys in the 'runtimeConfig' dictionary - // of the plugin's stdin data. libcni will ensure that only keys - // in this map which match the capabilities of the plugin are passed - // to the plugin - CapabilityArgs map[string]interface{} - - // DEPRECATED. Will be removed in a future release. - CacheDir string -} - -// Use PluginConfig instead of NetworkConfig, the NetworkConfig -// backwards-compat alias will be removed in a future release. -type NetworkConfig = PluginConfig - -type PluginConfig struct { - Network *types.PluginConf - Bytes []byte -} - -type NetworkConfigList struct { - Name string - CNIVersion string - DisableCheck bool - DisableGC bool - LoadOnlyInlinedPlugins bool - Plugins []*PluginConfig - Bytes []byte -} - -type NetworkAttachment struct { - ContainerID string - Network string - IfName string - Config []byte - NetNS string - CniArgs [][2]string - CapabilityArgs map[string]interface{} -} - -type GCArgs struct { - ValidAttachments []types.GCAttachment -} - -type CNI interface { - AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error) - CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error - DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error - GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error) - GetNetworkListCachedConfig(net *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error) - - AddNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) (types.Result, error) - CheckNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error - DelNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error - GetNetworkCachedResult(net *PluginConfig, rt *RuntimeConf) (types.Result, error) - GetNetworkCachedConfig(net *PluginConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error) - - ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error) - ValidateNetwork(ctx context.Context, net *PluginConfig) ([]string, error) - - GCNetworkList(ctx context.Context, net *NetworkConfigList, args *GCArgs) error - GetStatusNetworkList(ctx context.Context, net *NetworkConfigList) error - - GetCachedAttachments(containerID string) ([]*NetworkAttachment, error) - - GetVersionInfo(ctx context.Context, pluginType string) (version.PluginInfo, error) -} - -type CNIConfig struct { - Path []string - exec invoke.Exec - cacheDir string -} - -// CNIConfig implements the CNI interface -var _ CNI = &CNIConfig{} - -// NewCNIConfig returns a new CNIConfig object that will search for plugins -// in the given paths and use the given exec interface to run those plugins, -// or if the exec interface is not given, will use a default exec handler. -func NewCNIConfig(path []string, exec invoke.Exec) *CNIConfig { - return NewCNIConfigWithCacheDir(path, "", exec) -} - -// NewCNIConfigWithCacheDir returns a new CNIConfig object that will search for plugins -// in the given paths use the given exec interface to run those plugins, -// or if the exec interface is not given, will use a default exec handler. -// The given cache directory will be used for temporary data storage when needed. -func NewCNIConfigWithCacheDir(path []string, cacheDir string, exec invoke.Exec) *CNIConfig { - return &CNIConfig{ - Path: path, - cacheDir: cacheDir, - exec: exec, - } -} - -func buildOneConfig(name, cniVersion string, orig *PluginConfig, prevResult types.Result, rt *RuntimeConf) (*PluginConfig, error) { - var err error - - inject := map[string]interface{}{ - "name": name, - "cniVersion": cniVersion, - } - // Add previous plugin result - if prevResult != nil { - inject["prevResult"] = prevResult - } - - // Ensure every config uses the same name and version - orig, err = InjectConf(orig, inject) - if err != nil { - return nil, err - } - if rt != nil { - return injectRuntimeConfig(orig, rt) - } - - return orig, nil -} - -// This function takes a libcni RuntimeConf structure and injects values into -// a "runtimeConfig" dictionary in the CNI network configuration JSON that -// will be passed to the plugin on stdin. -// -// Only "capabilities arguments" passed by the runtime are currently injected. -// These capabilities arguments are filtered through the plugin's advertised -// capabilities from its config JSON, and any keys in the CapabilityArgs -// matching plugin capabilities are added to the "runtimeConfig" dictionary -// sent to the plugin via JSON on stdin. For example, if the plugin's -// capabilities include "portMappings", and the CapabilityArgs map includes a -// "portMappings" key, that key and its value are added to the "runtimeConfig" -// dictionary to be passed to the plugin's stdin. -func injectRuntimeConfig(orig *PluginConfig, rt *RuntimeConf) (*PluginConfig, error) { - var err error - - rc := make(map[string]interface{}) - for capability, supported := range orig.Network.Capabilities { - if !supported { - continue - } - if data, ok := rt.CapabilityArgs[capability]; ok { - rc[capability] = data - } - } - - if len(rc) > 0 { - orig, err = InjectConf(orig, map[string]interface{}{"runtimeConfig": rc}) - if err != nil { - return nil, err - } - } - - return orig, nil -} - -// ensure we have a usable exec if the CNIConfig was not given one -func (c *CNIConfig) ensureExec() invoke.Exec { - if c.exec == nil { - c.exec = &invoke.DefaultExec{ - RawExec: &invoke.RawExec{Stderr: os.Stderr}, - PluginDecoder: version.PluginDecoder{}, - } - } - return c.exec -} - -type cachedInfo struct { - Kind string `json:"kind"` - ContainerID string `json:"containerId"` - Config []byte `json:"config"` - IfName string `json:"ifName"` - NetworkName string `json:"networkName"` - NetNS string `json:"netns,omitempty"` - CniArgs [][2]string `json:"cniArgs,omitempty"` - CapabilityArgs map[string]interface{} `json:"capabilityArgs,omitempty"` - RawResult map[string]interface{} `json:"result,omitempty"` - Result types.Result `json:"-"` -} - -// getCacheDir returns the cache directory in this order: -// 1) global cacheDir from CNIConfig object -// 2) deprecated cacheDir from RuntimeConf object -// 3) fall back to default cache directory -func (c *CNIConfig) getCacheDir(rt *RuntimeConf) string { - if c.cacheDir != "" { - return c.cacheDir - } - if rt.CacheDir != "" { - return rt.CacheDir - } - return CacheDir -} - -func (c *CNIConfig) getCacheFilePath(netName string, rt *RuntimeConf) (string, error) { - if netName == "" || rt.ContainerID == "" || rt.IfName == "" { - return "", fmt.Errorf("cache file path requires network name (%q), container ID (%q), and interface name (%q)", netName, rt.ContainerID, rt.IfName) - } - return filepath.Join(c.getCacheDir(rt), "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName)), nil -} - -func (c *CNIConfig) cacheAdd(result types.Result, config []byte, netName string, rt *RuntimeConf) error { - cached := cachedInfo{ - Kind: CNICacheV1, - ContainerID: rt.ContainerID, - Config: config, - IfName: rt.IfName, - NetworkName: netName, - NetNS: rt.NetNS, - CniArgs: rt.Args, - CapabilityArgs: rt.CapabilityArgs, - } - - // We need to get type.Result into cachedInfo as JSON map - // Marshal to []byte, then Unmarshal into cached.RawResult - data, err := json.Marshal(result) - if err != nil { - return err - } - - err = json.Unmarshal(data, &cached.RawResult) - if err != nil { - return err - } - - newBytes, err := json.Marshal(&cached) - if err != nil { - return err - } - - fname, err := c.getCacheFilePath(netName, rt) - if err != nil { - return err - } - if err := os.MkdirAll(filepath.Dir(fname), 0o700); err != nil { - return err - } - - return os.WriteFile(fname, newBytes, 0o600) -} - -func (c *CNIConfig) cacheDel(netName string, rt *RuntimeConf) error { - fname, err := c.getCacheFilePath(netName, rt) - if err != nil { - // Ignore error - return nil - } - return os.Remove(fname) -} - -func (c *CNIConfig) getCachedConfig(netName string, rt *RuntimeConf) ([]byte, *RuntimeConf, error) { - var bytes []byte - - fname, err := c.getCacheFilePath(netName, rt) - if err != nil { - return nil, nil, err - } - bytes, err = os.ReadFile(fname) - if err != nil { - // Ignore read errors; the cached result may not exist on-disk - return nil, nil, nil - } - - unmarshaled := cachedInfo{} - if err := json.Unmarshal(bytes, &unmarshaled); err != nil { - return nil, nil, fmt.Errorf("failed to unmarshal cached network %q config: %w", netName, err) - } - if unmarshaled.Kind != CNICacheV1 { - return nil, nil, fmt.Errorf("read cached network %q config has wrong kind: %v", netName, unmarshaled.Kind) - } - - newRt := *rt - if unmarshaled.CniArgs != nil { - newRt.Args = unmarshaled.CniArgs - } - newRt.CapabilityArgs = unmarshaled.CapabilityArgs - - return unmarshaled.Config, &newRt, nil -} - -func (c *CNIConfig) getLegacyCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) { - fname, err := c.getCacheFilePath(netName, rt) - if err != nil { - return nil, err - } - data, err := os.ReadFile(fname) - if err != nil { - // Ignore read errors; the cached result may not exist on-disk - return nil, nil - } - - // Load the cached result - result, err := create.CreateFromBytes(data) - if err != nil { - return nil, err - } - - // Convert to the config version to ensure plugins get prevResult - // in the same version as the config. The cached result version - // should match the config version unless the config was changed - // while the container was running. - result, err = result.GetAsVersion(cniVersion) - if err != nil { - return nil, fmt.Errorf("failed to convert cached result to config version %q: %w", cniVersion, err) - } - return result, nil -} - -func (c *CNIConfig) getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) { - fname, err := c.getCacheFilePath(netName, rt) - if err != nil { - return nil, err - } - fdata, err := os.ReadFile(fname) - if err != nil { - // Ignore read errors; the cached result may not exist on-disk - return nil, nil - } - - cachedInfo := cachedInfo{} - if err := json.Unmarshal(fdata, &cachedInfo); err != nil || cachedInfo.Kind != CNICacheV1 { - return c.getLegacyCachedResult(netName, cniVersion, rt) - } - - newBytes, err := json.Marshal(&cachedInfo.RawResult) - if err != nil { - return nil, fmt.Errorf("failed to marshal cached network %q config: %w", netName, err) - } - - // Load the cached result - result, err := create.CreateFromBytes(newBytes) - if err != nil { - return nil, err - } - - // Convert to the config version to ensure plugins get prevResult - // in the same version as the config. The cached result version - // should match the config version unless the config was changed - // while the container was running. - result, err = result.GetAsVersion(cniVersion) - if err != nil { - return nil, fmt.Errorf("failed to convert cached result to config version %q: %w", cniVersion, err) - } - return result, nil -} - -// GetNetworkListCachedResult returns the cached Result of the previous -// AddNetworkList() operation for a network list, or an error. -func (c *CNIConfig) GetNetworkListCachedResult(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) { - return c.getCachedResult(list.Name, list.CNIVersion, rt) -} - -// GetNetworkCachedResult returns the cached Result of the previous -// AddNetwork() operation for a network, or an error. -func (c *CNIConfig) GetNetworkCachedResult(net *PluginConfig, rt *RuntimeConf) (types.Result, error) { - return c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt) -} - -// GetNetworkListCachedConfig copies the input RuntimeConf to output -// RuntimeConf with fields updated with info from the cached Config. -func (c *CNIConfig) GetNetworkListCachedConfig(list *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error) { - return c.getCachedConfig(list.Name, rt) -} - -// GetNetworkCachedConfig copies the input RuntimeConf to output -// RuntimeConf with fields updated with info from the cached Config. -func (c *CNIConfig) GetNetworkCachedConfig(net *PluginConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error) { - return c.getCachedConfig(net.Network.Name, rt) -} - -// GetCachedAttachments returns a list of network attachments from the cache. -// The returned list will be filtered by the containerID if the value is not empty. -func (c *CNIConfig) GetCachedAttachments(containerID string) ([]*NetworkAttachment, error) { - dirPath := filepath.Join(c.getCacheDir(&RuntimeConf{}), "results") - entries, err := os.ReadDir(dirPath) - if err != nil { - if os.IsNotExist(err) { - return nil, nil - } - return nil, err - } - - fileNames := make([]string, 0, len(entries)) - for _, e := range entries { - fileNames = append(fileNames, e.Name()) - } - sort.Strings(fileNames) - - attachments := []*NetworkAttachment{} - for _, fname := range fileNames { - if len(containerID) > 0 { - part := fmt.Sprintf("-%s-", containerID) - pos := strings.Index(fname, part) - if pos <= 0 || pos+len(part) >= len(fname) { - continue - } - } - - cacheFile := filepath.Join(dirPath, fname) - bytes, err := os.ReadFile(cacheFile) - if err != nil { - continue - } - - cachedInfo := cachedInfo{} - - if err := json.Unmarshal(bytes, &cachedInfo); err != nil { - continue - } - if cachedInfo.Kind != CNICacheV1 { - continue - } - if len(containerID) > 0 && cachedInfo.ContainerID != containerID { - continue - } - if cachedInfo.IfName == "" || cachedInfo.NetworkName == "" { - continue - } - - attachments = append(attachments, &NetworkAttachment{ - ContainerID: cachedInfo.ContainerID, - Network: cachedInfo.NetworkName, - IfName: cachedInfo.IfName, - Config: cachedInfo.Config, - NetNS: cachedInfo.NetNS, - CniArgs: cachedInfo.CniArgs, - CapabilityArgs: cachedInfo.CapabilityArgs, - }) - } - return attachments, nil -} - -func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) { - c.ensureExec() - pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path) - if err != nil { - return nil, err - } - if err := utils.ValidateContainerID(rt.ContainerID); err != nil { - return nil, err - } - if err := utils.ValidateNetworkName(name); err != nil { - return nil, err - } - if err := utils.ValidateInterfaceName(rt.IfName); err != nil { - return nil, err - } - - newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt) - if err != nil { - return nil, err - } - - return invoke.ExecPluginWithResult(ctx, pluginPath, newConf.Bytes, c.args("ADD", rt), c.exec) -} - -// AddNetworkList executes a sequence of plugins with the ADD command -func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) { - var err error - var result types.Result - for _, net := range list.Plugins { - result, err = c.addNetwork(ctx, list.Name, list.CNIVersion, net, result, rt) - if err != nil { - return nil, fmt.Errorf("plugin %s failed (add): %w", pluginDescription(net.Network), err) - } - } - - if err = c.cacheAdd(result, list.Bytes, list.Name, rt); err != nil { - return nil, fmt.Errorf("failed to set network %q cached result: %w", list.Name, err) - } - - return result, nil -} - -func (c *CNIConfig) checkNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) error { - c.ensureExec() - pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path) - if err != nil { - return err - } - - newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt) - if err != nil { - return err - } - - return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("CHECK", rt), c.exec) -} - -// CheckNetworkList executes a sequence of plugins with the CHECK command -func (c *CNIConfig) CheckNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error { - // CHECK was added in CNI spec version 0.4.0 and higher - if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil { - return err - } else if !gtet { - return fmt.Errorf("configuration version %q %w", list.CNIVersion, ErrorCheckNotSupp) - } - - if list.DisableCheck { - return nil - } - - cachedResult, err := c.getCachedResult(list.Name, list.CNIVersion, rt) - if err != nil { - return fmt.Errorf("failed to get network %q cached result: %w", list.Name, err) - } - - for _, net := range list.Plugins { - if err := c.checkNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil { - return err - } - } - - return nil -} - -func (c *CNIConfig) delNetwork(ctx context.Context, name, cniVersion string, net *PluginConfig, prevResult types.Result, rt *RuntimeConf) error { - c.ensureExec() - pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path) - if err != nil { - return err - } - - newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt) - if err != nil { - return err - } - - return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("DEL", rt), c.exec) -} - -// DelNetworkList executes a sequence of plugins with the DEL command -func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error { - var cachedResult types.Result - - // Cached result on DEL was added in CNI spec version 0.4.0 and higher - if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil { - return err - } else if gtet { - if cachedResult, err = c.getCachedResult(list.Name, list.CNIVersion, rt); err != nil { - _ = c.cacheDel(list.Name, rt) - cachedResult = nil - } - } - - for i := len(list.Plugins) - 1; i >= 0; i-- { - net := list.Plugins[i] - if err := c.delNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil { - return fmt.Errorf("plugin %s failed (delete): %w", pluginDescription(net.Network), err) - } - } - - _ = c.cacheDel(list.Name, rt) - - return nil -} - -func pluginDescription(net *types.PluginConf) string { - if net == nil { - return "" - } - pluginType := net.Type - out := fmt.Sprintf("type=%q", pluginType) - name := net.Name - if name != "" { - out += fmt.Sprintf(" name=%q", name) - } - return out -} - -// AddNetwork executes the plugin with the ADD command -func (c *CNIConfig) AddNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) (types.Result, error) { - result, err := c.addNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, nil, rt) - if err != nil { - return nil, err - } - - if err = c.cacheAdd(result, net.Bytes, net.Network.Name, rt); err != nil { - return nil, fmt.Errorf("failed to set network %q cached result: %w", net.Network.Name, err) - } - - return result, nil -} - -// CheckNetwork executes the plugin with the CHECK command -func (c *CNIConfig) CheckNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error { - // CHECK was added in CNI spec version 0.4.0 and higher - if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil { - return err - } else if !gtet { - return fmt.Errorf("configuration version %q %w", net.Network.CNIVersion, ErrorCheckNotSupp) - } - - cachedResult, err := c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt) - if err != nil { - return fmt.Errorf("failed to get network %q cached result: %w", net.Network.Name, err) - } - return c.checkNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt) -} - -// DelNetwork executes the plugin with the DEL command -func (c *CNIConfig) DelNetwork(ctx context.Context, net *PluginConfig, rt *RuntimeConf) error { - var cachedResult types.Result - - // Cached result on DEL was added in CNI spec version 0.4.0 and higher - if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil { - return err - } else if gtet { - cachedResult, err = c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt) - if err != nil { - return fmt.Errorf("failed to get network %q cached result: %w", net.Network.Name, err) - } - } - - if err := c.delNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt); err != nil { - return err - } - _ = c.cacheDel(net.Network.Name, rt) - return nil -} - -// ValidateNetworkList checks that a configuration is reasonably valid. -// - all the specified plugins exist on disk -// - every plugin supports the desired version. -// -// Returns a list of all capabilities supported by the configuration, or error -func (c *CNIConfig) ValidateNetworkList(ctx context.Context, list *NetworkConfigList) ([]string, error) { - version := list.CNIVersion - - // holding map for seen caps (in case of duplicates) - caps := map[string]interface{}{} - - errs := []error{} - for _, net := range list.Plugins { - if err := c.validatePlugin(ctx, net.Network.Type, version); err != nil { - errs = append(errs, err) - } - for c, enabled := range net.Network.Capabilities { - if !enabled { - continue - } - caps[c] = struct{}{} - } - } - - if len(errs) > 0 { - return nil, fmt.Errorf("%v", errs) - } - - // make caps list - cc := make([]string, 0, len(caps)) - for c := range caps { - cc = append(cc, c) - } - - return cc, nil -} - -// ValidateNetwork checks that a configuration is reasonably valid. -// It uses the same logic as ValidateNetworkList) -// Returns a list of capabilities -func (c *CNIConfig) ValidateNetwork(ctx context.Context, net *PluginConfig) ([]string, error) { - caps := []string{} - for c, ok := range net.Network.Capabilities { - if ok { - caps = append(caps, c) - } - } - if err := c.validatePlugin(ctx, net.Network.Type, net.Network.CNIVersion); err != nil { - return nil, err - } - return caps, nil -} - -// validatePlugin checks that an individual plugin's configuration is sane -func (c *CNIConfig) validatePlugin(ctx context.Context, pluginName, expectedVersion string) error { - c.ensureExec() - pluginPath, err := c.exec.FindInPath(pluginName, c.Path) - if err != nil { - return err - } - if expectedVersion == "" { - expectedVersion = "0.1.0" - } - - vi, err := invoke.GetVersionInfo(ctx, pluginPath, c.exec) - if err != nil { - return err - } - for _, vers := range vi.SupportedVersions() { - if vers == expectedVersion { - return nil - } - } - return fmt.Errorf("plugin %s does not support config version %q", pluginName, expectedVersion) -} - -// GetVersionInfo reports which versions of the CNI spec are supported by -// the given plugin. -func (c *CNIConfig) GetVersionInfo(ctx context.Context, pluginType string) (version.PluginInfo, error) { - c.ensureExec() - pluginPath, err := c.exec.FindInPath(pluginType, c.Path) - if err != nil { - return nil, err - } - - return invoke.GetVersionInfo(ctx, pluginPath, c.exec) -} - -// GCNetworkList will do two things -// - dump the list of cached attachments, and issue deletes as necessary -// - issue a GC to the underlying plugins (if the version is high enough) -func (c *CNIConfig) GCNetworkList(ctx context.Context, list *NetworkConfigList, args *GCArgs) error { - // If DisableGC is set, then don't bother GCing at all. - if list.DisableGC { - return nil - } - - // First, get the list of cached attachments - cachedAttachments, err := c.GetCachedAttachments("") - if err != nil { - return nil - } - - var validAttachments map[types.GCAttachment]interface{} - if args != nil { - validAttachments = make(map[types.GCAttachment]interface{}, len(args.ValidAttachments)) - for _, a := range args.ValidAttachments { - validAttachments[a] = nil - } - } - - var errs []error - - for _, cachedAttachment := range cachedAttachments { - if cachedAttachment.Network != list.Name { - continue - } - // we found this attachment - gca := types.GCAttachment{ - ContainerID: cachedAttachment.ContainerID, - IfName: cachedAttachment.IfName, - } - if _, ok := validAttachments[gca]; ok { - continue - } - // otherwise, this attachment wasn't valid and we should issue a CNI DEL - rt := RuntimeConf{ - ContainerID: cachedAttachment.ContainerID, - NetNS: cachedAttachment.NetNS, - IfName: cachedAttachment.IfName, - Args: cachedAttachment.CniArgs, - CapabilityArgs: cachedAttachment.CapabilityArgs, - } - if err := c.DelNetworkList(ctx, list, &rt); err != nil { - errs = append(errs, fmt.Errorf("failed to delete stale attachment %s %s: %w", rt.ContainerID, rt.IfName, err)) - } - } - - // now, if the version supports it, issue a GC - if gt, _ := version.GreaterThanOrEqualTo(list.CNIVersion, "1.1.0"); gt { - inject := map[string]interface{}{ - "name": list.Name, - "cniVersion": list.CNIVersion, - } - if args != nil { - inject["cni.dev/valid-attachments"] = args.ValidAttachments - // #1101: spec used incorrect variable name - inject["cni.dev/attachments"] = args.ValidAttachments - } - - for _, plugin := range list.Plugins { - // build config here - pluginConfig, err := InjectConf(plugin, inject) - if err != nil { - errs = append(errs, fmt.Errorf("failed to generate configuration to GC plugin %s: %w", plugin.Network.Type, err)) - } - if err := c.gcNetwork(ctx, pluginConfig); err != nil { - errs = append(errs, fmt.Errorf("failed to GC plugin %s: %w", plugin.Network.Type, err)) - } - } - } - - return errors.Join(errs...) -} - -func (c *CNIConfig) gcNetwork(ctx context.Context, net *PluginConfig) error { - c.ensureExec() - pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path) - if err != nil { - return err - } - args := c.args("GC", &RuntimeConf{}) - - return invoke.ExecPluginWithoutResult(ctx, pluginPath, net.Bytes, args, c.exec) -} - -func (c *CNIConfig) GetStatusNetworkList(ctx context.Context, list *NetworkConfigList) error { - // If the version doesn't support status, abort. - if gt, _ := version.GreaterThanOrEqualTo(list.CNIVersion, "1.1.0"); !gt { - return nil - } - - inject := map[string]interface{}{ - "name": list.Name, - "cniVersion": list.CNIVersion, - } - - for _, plugin := range list.Plugins { - // build config here - pluginConfig, err := InjectConf(plugin, inject) - if err != nil { - return fmt.Errorf("failed to generate configuration to get plugin STATUS %s: %w", plugin.Network.Type, err) - } - if err := c.getStatusNetwork(ctx, pluginConfig); err != nil { - return err // Don't collect errors here, so we return a clean error code. - } - } - return nil -} - -func (c *CNIConfig) getStatusNetwork(ctx context.Context, net *PluginConfig) error { - c.ensureExec() - pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path) - if err != nil { - return err - } - args := c.args("STATUS", &RuntimeConf{}) - - return invoke.ExecPluginWithoutResult(ctx, pluginPath, net.Bytes, args, c.exec) -} - -// ===== -func (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args { - return &invoke.Args{ - Command: action, - ContainerID: rt.ContainerID, - NetNS: rt.NetNS, - PluginArgs: rt.Args, - IfName: rt.IfName, - Path: strings.Join(c.Path, string(os.PathListSeparator)), - } -} diff --git a/vendor/github.com/containernetworking/cni/libcni/conf.go b/vendor/github.com/containernetworking/cni/libcni/conf.go deleted file mode 100644 index 7f8482e75e..0000000000 --- a/vendor/github.com/containernetworking/cni/libcni/conf.go +++ /dev/null @@ -1,445 +0,0 @@ -// Copyright 2015 CNI authors -// -// 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 -// -// http://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. - -package libcni - -import ( - "encoding/json" - "errors" - "fmt" - "os" - "path/filepath" - "slices" - "sort" - "strings" - - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/version" -) - -type NotFoundError struct { - Dir string - Name string -} - -func (e NotFoundError) Error() string { - return fmt.Sprintf(`no net configuration with name "%s" in %s`, e.Name, e.Dir) -} - -type NoConfigsFoundError struct { - Dir string -} - -func (e NoConfigsFoundError) Error() string { - return fmt.Sprintf(`no net configurations found in %s`, e.Dir) -} - -// This will not validate that the plugins actually belong to the netconfig by ensuring -// that they are loaded from a directory named after the networkName, relative to the network config. -// -// Since here we are just accepting raw bytes, the caller is responsible for ensuring that the plugin -// config provided here actually "belongs" to the networkconfig in question. -func NetworkPluginConfFromBytes(pluginConfBytes []byte) (*PluginConfig, error) { - // TODO why are we creating a struct that holds both the byte representation and the deserialized - // representation, and returning that, instead of just returning the deserialized representation? - conf := &PluginConfig{Bytes: pluginConfBytes, Network: &types.PluginConf{}} - if err := json.Unmarshal(pluginConfBytes, conf.Network); err != nil { - return nil, fmt.Errorf("error parsing configuration: %w", err) - } - if conf.Network.Type == "" { - return nil, fmt.Errorf("error parsing configuration: missing 'type'") - } - return conf, nil -} - -// Given a path to a directory containing a network configuration, and the name of a network, -// loads all plugin definitions found at path `networkConfPath/networkName/*.conf` -func NetworkPluginConfsFromFiles(networkConfPath, networkName string) ([]*PluginConfig, error) { - var pConfs []*PluginConfig - - pluginConfPath := filepath.Join(networkConfPath, networkName) - - pluginConfFiles, err := ConfFiles(pluginConfPath, []string{".conf"}) - if err != nil { - return nil, fmt.Errorf("failed to read plugin config files in %s: %w", pluginConfPath, err) - } - - for _, pluginConfFile := range pluginConfFiles { - pluginConfBytes, err := os.ReadFile(pluginConfFile) - if err != nil { - return nil, fmt.Errorf("error reading %s: %w", pluginConfFile, err) - } - pluginConf, err := NetworkPluginConfFromBytes(pluginConfBytes) - if err != nil { - return nil, err - } - pConfs = append(pConfs, pluginConf) - } - return pConfs, nil -} - -func NetworkConfFromBytes(confBytes []byte) (*NetworkConfigList, error) { - rawList := make(map[string]interface{}) - if err := json.Unmarshal(confBytes, &rawList); err != nil { - return nil, fmt.Errorf("error parsing configuration list: %w", err) - } - - rawName, ok := rawList["name"] - if !ok { - return nil, fmt.Errorf("error parsing configuration list: no name") - } - name, ok := rawName.(string) - if !ok { - return nil, fmt.Errorf("error parsing configuration list: invalid name type %T", rawName) - } - - var cniVersion string - rawVersion, ok := rawList["cniVersion"] - if ok { - cniVersion, ok = rawVersion.(string) - if !ok { - return nil, fmt.Errorf("error parsing configuration list: invalid cniVersion type %T", rawVersion) - } - } - - rawVersions, ok := rawList["cniVersions"] - if ok { - // Parse the current package CNI version - rvs, ok := rawVersions.([]interface{}) - if !ok { - return nil, fmt.Errorf("error parsing configuration list: invalid type for cniVersions: %T", rvs) - } - vs := make([]string, 0, len(rvs)) - for i, rv := range rvs { - v, ok := rv.(string) - if !ok { - return nil, fmt.Errorf("error parsing configuration list: invalid type for cniVersions index %d: %T", i, rv) - } - gt, err := version.GreaterThan(v, version.Current()) - if err != nil { - return nil, fmt.Errorf("error parsing configuration list: invalid cniVersions entry %s at index %d: %w", v, i, err) - } else if !gt { - // Skip versions "greater" than this implementation of the spec - vs = append(vs, v) - } - } - - // if cniVersion was already set, append it to the list for sorting. - if cniVersion != "" { - gt, err := version.GreaterThan(cniVersion, version.Current()) - if err != nil { - return nil, fmt.Errorf("error parsing configuration list: invalid cniVersion %s: %w", cniVersion, err) - } else if !gt { - // ignore any versions higher than the current implemented spec version - vs = append(vs, cniVersion) - } - } - slices.SortFunc[[]string](vs, func(v1, v2 string) int { - if v1 == v2 { - return 0 - } - if gt, _ := version.GreaterThan(v1, v2); gt { - return 1 - } - return -1 - }) - if len(vs) > 0 { - cniVersion = vs[len(vs)-1] - } - } - - readBool := func(key string) (bool, error) { - rawVal, ok := rawList[key] - if !ok { - return false, nil - } - if b, ok := rawVal.(bool); ok { - return b, nil - } - - s, ok := rawVal.(string) - if !ok { - return false, fmt.Errorf("error parsing configuration list: invalid type %T for %s", rawVal, key) - } - s = strings.ToLower(s) - switch s { - case "false": - return false, nil - case "true": - return true, nil - } - return false, fmt.Errorf("error parsing configuration list: invalid value %q for %s", s, key) - } - - disableCheck, err := readBool("disableCheck") - if err != nil { - return nil, err - } - - disableGC, err := readBool("disableGC") - if err != nil { - return nil, err - } - - loadOnlyInlinedPlugins, err := readBool("loadOnlyInlinedPlugins") - if err != nil { - return nil, err - } - - list := &NetworkConfigList{ - Name: name, - DisableCheck: disableCheck, - DisableGC: disableGC, - LoadOnlyInlinedPlugins: loadOnlyInlinedPlugins, - CNIVersion: cniVersion, - Bytes: confBytes, - } - - var plugins []interface{} - plug, ok := rawList["plugins"] - // We can have a `plugins` list key in the main conf, - // We can also have `loadOnlyInlinedPlugins == true` - // - // If `plugins` is there, then `loadOnlyInlinedPlugins` can be true - // - // If plugins is NOT there, then `loadOnlyInlinedPlugins` cannot be true - // - // We have to have at least some plugins. - if !ok && loadOnlyInlinedPlugins { - return nil, fmt.Errorf("error parsing configuration list: `loadOnlyInlinedPlugins` is true, and no 'plugins' key") - } else if !ok && !loadOnlyInlinedPlugins { - return list, nil - } - - plugins, ok = plug.([]interface{}) - if !ok { - return nil, fmt.Errorf("error parsing configuration list: invalid 'plugins' type %T", plug) - } - if len(plugins) == 0 { - return nil, fmt.Errorf("error parsing configuration list: no plugins in list") - } - - for i, conf := range plugins { - newBytes, err := json.Marshal(conf) - if err != nil { - return nil, fmt.Errorf("failed to marshal plugin config %d: %w", i, err) - } - netConf, err := ConfFromBytes(newBytes) - if err != nil { - return nil, fmt.Errorf("failed to parse plugin config %d: %w", i, err) - } - list.Plugins = append(list.Plugins, netConf) - } - return list, nil -} - -func NetworkConfFromFile(filename string) (*NetworkConfigList, error) { - bytes, err := os.ReadFile(filename) - if err != nil { - return nil, fmt.Errorf("error reading %s: %w", filename, err) - } - - conf, err := NetworkConfFromBytes(bytes) - if err != nil { - return nil, err - } - - if !conf.LoadOnlyInlinedPlugins { - plugins, err := NetworkPluginConfsFromFiles(filepath.Dir(filename), conf.Name) - if err != nil { - return nil, err - } - conf.Plugins = append(conf.Plugins, plugins...) - } - - if len(conf.Plugins) == 0 { - // Having 0 plugins for a given network is not necessarily a problem, - // but return as error for caller to decide, since they tried to load - return nil, fmt.Errorf("no plugin configs found") - } - return conf, nil -} - -// Deprecated: This file format is no longer supported, use NetworkConfXXX and NetworkPluginXXX functions -func ConfFromBytes(bytes []byte) (*NetworkConfig, error) { - return NetworkPluginConfFromBytes(bytes) -} - -// Deprecated: This file format is no longer supported, use NetworkConfXXX and NetworkPluginXXX functions -func ConfFromFile(filename string) (*NetworkConfig, error) { - bytes, err := os.ReadFile(filename) - if err != nil { - return nil, fmt.Errorf("error reading %s: %w", filename, err) - } - return ConfFromBytes(bytes) -} - -func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) { - return NetworkConfFromBytes(bytes) -} - -func ConfListFromFile(filename string) (*NetworkConfigList, error) { - return NetworkConfFromFile(filename) -} - -// ConfFiles simply returns a slice of all files in the provided directory -// with extensions matching the provided set. -func ConfFiles(dir string, extensions []string) ([]string, error) { - // In part, adapted from rkt/networking/podenv.go#listFiles - files, err := os.ReadDir(dir) - switch { - case err == nil: // break - case os.IsNotExist(err): - // If folder not there, return no error - only return an - // error if we cannot read contents or there are no contents. - return nil, nil - default: - return nil, err - } - - confFiles := []string{} - for _, f := range files { - if f.IsDir() { - continue - } - fileExt := filepath.Ext(f.Name()) - for _, ext := range extensions { - if fileExt == ext { - confFiles = append(confFiles, filepath.Join(dir, f.Name())) - } - } - } - return confFiles, nil -} - -// Deprecated: This file format is no longer supported, use NetworkConfXXX and NetworkPluginXXX functions -func LoadConf(dir, name string) (*NetworkConfig, error) { - files, err := ConfFiles(dir, []string{".conf", ".json"}) - switch { - case err != nil: - return nil, err - case len(files) == 0: - return nil, NoConfigsFoundError{Dir: dir} - } - sort.Strings(files) - - for _, confFile := range files { - conf, err := ConfFromFile(confFile) - if err != nil { - return nil, err - } - if conf.Network.Name == name { - return conf, nil - } - } - return nil, NotFoundError{dir, name} -} - -func LoadConfList(dir, name string) (*NetworkConfigList, error) { - return LoadNetworkConf(dir, name) -} - -// LoadNetworkConf looks at all the network configs in a given dir, -// loads and parses them all, and returns the first one with an extension of `.conf` -// that matches the provided network name predicate. -func LoadNetworkConf(dir, name string) (*NetworkConfigList, error) { - // TODO this .conflist/.conf extension thing is confusing and inexact - // for implementors. We should pick one extension for everything and stick with it. - files, err := ConfFiles(dir, []string{".conflist"}) - if err != nil { - return nil, err - } - sort.Strings(files) - - for _, confFile := range files { - conf, err := NetworkConfFromFile(confFile) - if err != nil { - return nil, err - } - if conf.Name == name { - return conf, nil - } - } - - // Deprecated: Try and load a network configuration file (instead of list) - // from the same name, then upconvert. - singleConf, err := LoadConf(dir, name) - if err != nil { - // A little extra logic so the error makes sense - var ncfErr NoConfigsFoundError - if len(files) != 0 && errors.As(err, &ncfErr) { - // Config lists found but no config files found - return nil, NotFoundError{dir, name} - } - - return nil, err - } - return ConfListFromConf(singleConf) -} - -// InjectConf takes a PluginConfig and inserts additional values into it, ensuring the result is serializable. -func InjectConf(original *PluginConfig, newValues map[string]interface{}) (*PluginConfig, error) { - config := make(map[string]interface{}) - err := json.Unmarshal(original.Bytes, &config) - if err != nil { - return nil, fmt.Errorf("unmarshal existing network bytes: %w", err) - } - - for key, value := range newValues { - if key == "" { - return nil, fmt.Errorf("keys cannot be empty") - } - - if value == nil { - return nil, fmt.Errorf("key '%s' value must not be nil", key) - } - - config[key] = value - } - - newBytes, err := json.Marshal(config) - if err != nil { - return nil, err - } - - return NetworkPluginConfFromBytes(newBytes) -} - -// ConfListFromConf "upconverts" a network config in to a NetworkConfigList, -// with the single network as the only entry in the list. -// -// Deprecated: Non-conflist file formats are unsupported, use NetworkConfXXX and NetworkPluginXXX functions -func ConfListFromConf(original *PluginConfig) (*NetworkConfigList, error) { - // Re-deserialize the config's json, then make a raw map configlist. - // This may seem a bit strange, but it's to make the Bytes fields - // actually make sense. Otherwise, the generated json is littered with - // golang default values. - - rawConfig := make(map[string]interface{}) - if err := json.Unmarshal(original.Bytes, &rawConfig); err != nil { - return nil, err - } - - rawConfigList := map[string]interface{}{ - "name": original.Network.Name, - "cniVersion": original.Network.CNIVersion, - "plugins": []interface{}{rawConfig}, - } - - b, err := json.Marshal(rawConfigList) - if err != nil { - return nil, err - } - return ConfListFromBytes(b) -} diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/args.go b/vendor/github.com/containernetworking/cni/pkg/invoke/args.go deleted file mode 100644 index 3cdb4bc8da..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/args.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2015 CNI authors -// -// 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 -// -// http://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. - -package invoke - -import ( - "fmt" - "os" - "strings" -) - -type CNIArgs interface { - // For use with os/exec; i.e., return nil to inherit the - // environment from this process - // For use in delegation; inherit the environment from this - // process and allow overrides - AsEnv() []string -} - -type inherited struct{} - -var inheritArgsFromEnv inherited - -func (*inherited) AsEnv() []string { - return nil -} - -func ArgsFromEnv() CNIArgs { - return &inheritArgsFromEnv -} - -type Args struct { - Command string - ContainerID string - NetNS string - PluginArgs [][2]string - PluginArgsStr string - IfName string - Path string -} - -// Args implements the CNIArgs interface -var _ CNIArgs = &Args{} - -func (args *Args) AsEnv() []string { - env := os.Environ() - pluginArgsStr := args.PluginArgsStr - if pluginArgsStr == "" { - pluginArgsStr = stringify(args.PluginArgs) - } - - // Duplicated values which come first will be overridden, so we must put the - // custom values in the end to avoid being overridden by the process environments. - env = append(env, - "CNI_COMMAND="+args.Command, - "CNI_CONTAINERID="+args.ContainerID, - "CNI_NETNS="+args.NetNS, - "CNI_ARGS="+pluginArgsStr, - "CNI_IFNAME="+args.IfName, - "CNI_PATH="+args.Path, - ) - return dedupEnv(env) -} - -// taken from rkt/networking/net_plugin.go -func stringify(pluginArgs [][2]string) string { - entries := make([]string, len(pluginArgs)) - - for i, kv := range pluginArgs { - entries[i] = strings.Join(kv[:], "=") - } - - return strings.Join(entries, ";") -} - -// DelegateArgs implements the CNIArgs interface -// used for delegation to inherit from environments -// and allow some overrides like CNI_COMMAND -var _ CNIArgs = &DelegateArgs{} - -type DelegateArgs struct { - Command string -} - -func (d *DelegateArgs) AsEnv() []string { - env := os.Environ() - - // The custom values should come in the end to override the existing - // process environment of the same key. - env = append(env, - "CNI_COMMAND="+d.Command, - ) - return dedupEnv(env) -} - -// dedupEnv returns a copy of env with any duplicates removed, in favor of later values. -// Items not of the normal environment "key=value" form are preserved unchanged. -func dedupEnv(env []string) []string { - out := make([]string, 0, len(env)) - envMap := map[string]string{} - - for _, kv := range env { - // find the first "=" in environment, if not, just keep it - eq := strings.Index(kv, "=") - if eq < 0 { - out = append(out, kv) - continue - } - envMap[kv[:eq]] = kv[eq+1:] - } - - for k, v := range envMap { - out = append(out, fmt.Sprintf("%s=%s", k, v)) - } - - return out -} diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go b/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go deleted file mode 100644 index c8b548e7c6..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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 -// -// http://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. - -package invoke - -import ( - "context" - "os" - "path/filepath" - - "github.com/containernetworking/cni/pkg/types" -) - -func delegateCommon(delegatePlugin string, exec Exec) (string, Exec, error) { - if exec == nil { - exec = defaultExec - } - - paths := filepath.SplitList(os.Getenv("CNI_PATH")) - pluginPath, err := exec.FindInPath(delegatePlugin, paths) - if err != nil { - return "", nil, err - } - - return pluginPath, exec, nil -} - -// DelegateAdd calls the given delegate plugin with the CNI ADD action and -// JSON configuration -func DelegateAdd(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) (types.Result, error) { - pluginPath, realExec, err := delegateCommon(delegatePlugin, exec) - if err != nil { - return nil, err - } - - // DelegateAdd will override the original "CNI_COMMAND" env from process with ADD - return ExecPluginWithResult(ctx, pluginPath, netconf, delegateArgs("ADD"), realExec) -} - -// DelegateCheck calls the given delegate plugin with the CNI CHECK action and -// JSON configuration -func DelegateCheck(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error { - return delegateNoResult(ctx, delegatePlugin, netconf, exec, "CHECK") -} - -func delegateNoResult(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec, verb string) error { - pluginPath, realExec, err := delegateCommon(delegatePlugin, exec) - if err != nil { - return err - } - - return ExecPluginWithoutResult(ctx, pluginPath, netconf, delegateArgs(verb), realExec) -} - -// DelegateDel calls the given delegate plugin with the CNI DEL action and -// JSON configuration -func DelegateDel(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error { - return delegateNoResult(ctx, delegatePlugin, netconf, exec, "DEL") -} - -// DelegateStatus calls the given delegate plugin with the CNI STATUS action and -// JSON configuration -func DelegateStatus(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error { - return delegateNoResult(ctx, delegatePlugin, netconf, exec, "STATUS") -} - -// DelegateGC calls the given delegate plugin with the CNI GC action and -// JSON configuration -func DelegateGC(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error { - return delegateNoResult(ctx, delegatePlugin, netconf, exec, "GC") -} - -// return CNIArgs used by delegation -func delegateArgs(action string) *DelegateArgs { - return &DelegateArgs{ - Command: action, - } -} diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go b/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go deleted file mode 100644 index a5e015fc92..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2015 CNI authors -// -// 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 -// -// http://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. - -package invoke - -import ( - "context" - "encoding/json" - "fmt" - "os" - - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/create" - "github.com/containernetworking/cni/pkg/version" -) - -// Exec is an interface encapsulates all operations that deal with finding -// and executing a CNI plugin. Tests may provide a fake implementation -// to avoid writing fake plugins to temporary directories during the test. -type Exec interface { - ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) - FindInPath(plugin string, paths []string) (string, error) - Decode(jsonBytes []byte) (version.PluginInfo, error) -} - -// Plugin must return result in same version as specified in netconf; but -// for backwards compatibility reasons if the result version is empty use -// config version (rather than technically correct 0.1.0). -// https://github.com/containernetworking/cni/issues/895 -func fixupResultVersion(netconf, result []byte) (string, []byte, error) { - versionDecoder := &version.ConfigDecoder{} - confVersion, err := versionDecoder.Decode(netconf) - if err != nil { - return "", nil, err - } - - var rawResult map[string]interface{} - if err := json.Unmarshal(result, &rawResult); err != nil { - return "", nil, fmt.Errorf("failed to unmarshal raw result: %w", err) - } - - // plugin output of "null" is successfully unmarshalled, but results in a nil - // map which causes a panic when the confVersion is assigned below. - if rawResult == nil { - rawResult = make(map[string]interface{}) - } - - // Manually decode Result version; we need to know whether its cniVersion - // is empty, while built-in decoders (correctly) substitute 0.1.0 for an - // empty version per the CNI spec. - if resultVerRaw, ok := rawResult["cniVersion"]; ok { - resultVer, ok := resultVerRaw.(string) - if ok && resultVer != "" { - return resultVer, result, nil - } - } - - // If the cniVersion is not present or empty, assume the result is - // the same CNI spec version as the config - rawResult["cniVersion"] = confVersion - newBytes, err := json.Marshal(rawResult) - if err != nil { - return "", nil, fmt.Errorf("failed to remarshal fixed result: %w", err) - } - - return confVersion, newBytes, nil -} - -// For example, a testcase could pass an instance of the following fakeExec -// object to ExecPluginWithResult() to verify the incoming stdin and environment -// and provide a tailored response: -// -// import ( -// "encoding/json" -// "path" -// "strings" -// ) -// -// type fakeExec struct { -// version.PluginDecoder -// } -// -// func (f *fakeExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) { -// net := &types.NetConf{} -// err := json.Unmarshal(stdinData, net) -// if err != nil { -// return nil, fmt.Errorf("failed to unmarshal configuration: %v", err) -// } -// pluginName := path.Base(pluginPath) -// if pluginName != net.Type { -// return nil, fmt.Errorf("plugin name %q did not match config type %q", pluginName, net.Type) -// } -// for _, e := range environ { -// // Check environment for forced failure request -// parts := strings.Split(e, "=") -// if len(parts) > 0 && parts[0] == "FAIL" { -// return nil, fmt.Errorf("failed to execute plugin %s", pluginName) -// } -// } -// return []byte("{\"CNIVersion\":\"0.4.0\"}"), nil -// } -// -// func (f *fakeExec) FindInPath(plugin string, paths []string) (string, error) { -// if len(paths) > 0 { -// return path.Join(paths[0], plugin), nil -// } -// return "", fmt.Errorf("failed to find plugin %s in paths %v", plugin, paths) -// } - -func ExecPluginWithResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) (types.Result, error) { - if exec == nil { - exec = defaultExec - } - - stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv()) - if err != nil { - return nil, err - } - - resultVersion, fixedBytes, err := fixupResultVersion(netconf, stdoutBytes) - if err != nil { - return nil, err - } - - return create.Create(resultVersion, fixedBytes) -} - -func ExecPluginWithoutResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) error { - if exec == nil { - exec = defaultExec - } - _, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv()) - return err -} - -// GetVersionInfo returns the version information available about the plugin. -// For recent-enough plugins, it uses the information returned by the VERSION -// command. For older plugins which do not recognize that command, it reports -// version 0.1.0 -func GetVersionInfo(ctx context.Context, pluginPath string, exec Exec) (version.PluginInfo, error) { - if exec == nil { - exec = defaultExec - } - args := &Args{ - Command: "VERSION", - - // set fake values required by plugins built against an older version of skel - NetNS: "dummy", - IfName: "dummy", - Path: "dummy", - } - stdin := []byte(fmt.Sprintf(`{"cniVersion":%q}`, version.Current())) - stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, stdin, args.AsEnv()) - if err != nil { - if err.Error() == "unknown CNI_COMMAND: VERSION" { - return version.PluginSupports("0.1.0"), nil - } - return nil, err - } - - return exec.Decode(stdoutBytes) -} - -// DefaultExec is an object that implements the Exec interface which looks -// for and executes plugins from disk. -type DefaultExec struct { - *RawExec - version.PluginDecoder -} - -// DefaultExec implements the Exec interface -var _ Exec = &DefaultExec{} - -var defaultExec = &DefaultExec{ - RawExec: &RawExec{Stderr: os.Stderr}, -} diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/find.go b/vendor/github.com/containernetworking/cni/pkg/invoke/find.go deleted file mode 100644 index e62029eb78..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/find.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2015 CNI authors -// -// 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 -// -// http://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. - -package invoke - -import ( - "fmt" - "os" - "path/filepath" - "strings" -) - -// FindInPath returns the full path of the plugin by searching in the provided path -func FindInPath(plugin string, paths []string) (string, error) { - if plugin == "" { - return "", fmt.Errorf("no plugin name provided") - } - - if strings.ContainsRune(plugin, os.PathSeparator) { - return "", fmt.Errorf("invalid plugin name: %s", plugin) - } - - if len(paths) == 0 { - return "", fmt.Errorf("no paths provided") - } - - for _, path := range paths { - for _, fe := range ExecutableFileExtensions { - fullpath := filepath.Join(path, plugin) + fe - if fi, err := os.Stat(fullpath); err == nil && fi.Mode().IsRegular() { - return fullpath, nil - } - } - } - - return "", fmt.Errorf("failed to find plugin %q in path %s", plugin, paths) -} diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go b/vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go deleted file mode 100644 index ed0999bd0e..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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 -// -// http://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 darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build darwin dragonfly freebsd linux netbsd openbsd solaris - -package invoke - -// Valid file extensions for plugin executables. -var ExecutableFileExtensions = []string{""} diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/os_windows.go b/vendor/github.com/containernetworking/cni/pkg/invoke/os_windows.go deleted file mode 100644 index 7665125b13..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/os_windows.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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 -// -// http://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. - -package invoke - -// Valid file extensions for plugin executables. -var ExecutableFileExtensions = []string{".exe", ""} diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go b/vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go deleted file mode 100644 index 5ab5cc8857..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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 -// -// http://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. - -package invoke - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "os/exec" - "strings" - "time" - - "github.com/containernetworking/cni/pkg/types" -) - -type RawExec struct { - Stderr io.Writer -} - -func (e *RawExec) 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 - - // Retry the command on "text file busy" errors - for i := 0; i <= 5; i++ { - err := c.Run() - - // Command succeeded - if err == nil { - break - } - - // If the plugin is currently about to be written, then we wait a - // second and try it again - if strings.Contains(err.Error(), "text file busy") { - time.Sleep(time.Second) - continue - } - - // All other errors except than the busy text file - return nil, e.pluginErr(err, stdout.Bytes(), stderr.Bytes()) - } - - // Copy stderr to caller's buffer in case plugin printed to both - // stdout and stderr for some reason. Ignore failures as stderr is - // only informational. - if e.Stderr != nil && stderr.Len() > 0 { - _, _ = stderr.WriteTo(e.Stderr) - } - return stdout.Bytes(), nil -} - -func (e *RawExec) pluginErr(err error, stdout, stderr []byte) error { - emsg := types.Error{} - if len(stdout) == 0 { - if len(stderr) == 0 { - emsg.Msg = fmt.Sprintf("netplugin failed with no error message: %v", err) - } else { - emsg.Msg = fmt.Sprintf("netplugin failed: %q", string(stderr)) - } - } else if perr := json.Unmarshal(stdout, &emsg); perr != nil { - emsg.Msg = fmt.Sprintf("netplugin failed but error parsing its diagnostic message %q: %v", string(stdout), perr) - } - return &emsg -} - -func (e *RawExec) FindInPath(plugin string, paths []string) (string, error) { - return FindInPath(plugin, paths) -} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/020/types.go b/vendor/github.com/containernetworking/cni/pkg/types/020/types.go deleted file mode 100644 index 99b151ff24..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/types/020/types.go +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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 -// -// http://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. - -package types020 - -import ( - "encoding/json" - "fmt" - "io" - "net" - "os" - - "github.com/containernetworking/cni/pkg/types" - convert "github.com/containernetworking/cni/pkg/types/internal" -) - -const ImplementedSpecVersion string = "0.2.0" - -var supportedVersions = []string{"", "0.1.0", ImplementedSpecVersion} - -// Register converters for all versions less than the implemented spec version -func init() { - convert.RegisterConverter("0.1.0", []string{ImplementedSpecVersion}, convertFrom010) - convert.RegisterConverter(ImplementedSpecVersion, []string{"0.1.0"}, convertTo010) - - // Creator - convert.RegisterCreator(supportedVersions, NewResult) -} - -// Compatibility types for CNI version 0.1.0 and 0.2.0 - -// NewResult creates a new Result object from JSON data. The JSON data -// must be compatible with the CNI versions implemented by this type. -func NewResult(data []byte) (types.Result, error) { - result := &Result{} - if err := json.Unmarshal(data, result); err != nil { - return nil, err - } - for _, v := range supportedVersions { - if result.CNIVersion == v { - if result.CNIVersion == "" { - result.CNIVersion = "0.1.0" - } - return result, nil - } - } - return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q", - supportedVersions, result.CNIVersion) -} - -// GetResult converts the given Result object to the ImplementedSpecVersion -// and returns the concrete type or an error -func GetResult(r types.Result) (*Result, error) { - result020, err := convert.Convert(r, ImplementedSpecVersion) - if err != nil { - return nil, err - } - result, ok := result020.(*Result) - if !ok { - return nil, fmt.Errorf("failed to convert result") - } - return result, nil -} - -func convertFrom010(from types.Result, toVersion string) (types.Result, error) { - if toVersion != "0.2.0" { - panic("only converts to version 0.2.0") - } - fromResult := from.(*Result) - return &Result{ - CNIVersion: ImplementedSpecVersion, - IP4: fromResult.IP4.Copy(), - IP6: fromResult.IP6.Copy(), - DNS: *fromResult.DNS.Copy(), - }, nil -} - -func convertTo010(from types.Result, toVersion string) (types.Result, error) { - if toVersion != "0.1.0" { - panic("only converts to version 0.1.0") - } - fromResult := from.(*Result) - return &Result{ - CNIVersion: "0.1.0", - IP4: fromResult.IP4.Copy(), - IP6: fromResult.IP6.Copy(), - DNS: *fromResult.DNS.Copy(), - }, nil -} - -// Result is what gets returned from the plugin (via stdout) to the caller -type Result struct { - CNIVersion string `json:"cniVersion,omitempty"` - IP4 *IPConfig `json:"ip4,omitempty"` - IP6 *IPConfig `json:"ip6,omitempty"` - DNS types.DNS `json:"dns,omitempty"` -} - -func (r *Result) Version() string { - return r.CNIVersion -} - -func (r *Result) GetAsVersion(version string) (types.Result, error) { - // If the creator of the result did not set the CNIVersion, assume it - // should be the highest spec version implemented by this Result - if r.CNIVersion == "" { - r.CNIVersion = ImplementedSpecVersion - } - return convert.Convert(r, version) -} - -func (r *Result) Print() error { - return r.PrintTo(os.Stdout) -} - -func (r *Result) PrintTo(writer io.Writer) error { - data, err := json.MarshalIndent(r, "", " ") - if err != nil { - return err - } - _, err = writer.Write(data) - return err -} - -// IPConfig contains values necessary to configure an interface -type IPConfig struct { - IP net.IPNet - Gateway net.IP - Routes []types.Route -} - -func (i *IPConfig) Copy() *IPConfig { - if i == nil { - return nil - } - - var routes []types.Route - for _, fromRoute := range i.Routes { - routes = append(routes, *fromRoute.Copy()) - } - return &IPConfig{ - IP: i.IP, - Gateway: i.Gateway, - Routes: routes, - } -} - -// net.IPNet is not JSON (un)marshallable so this duality is needed -// for our custom IPNet type - -// JSON (un)marshallable types -type ipConfig struct { - IP types.IPNet `json:"ip"` - Gateway net.IP `json:"gateway,omitempty"` - Routes []types.Route `json:"routes,omitempty"` -} - -func (c *IPConfig) MarshalJSON() ([]byte, error) { - ipc := ipConfig{ - IP: types.IPNet(c.IP), - Gateway: c.Gateway, - Routes: c.Routes, - } - - return json.Marshal(ipc) -} - -func (c *IPConfig) UnmarshalJSON(data []byte) error { - ipc := ipConfig{} - if err := json.Unmarshal(data, &ipc); err != nil { - return err - } - - c.IP = net.IPNet(ipc.IP) - c.Gateway = ipc.Gateway - c.Routes = ipc.Routes - return nil -} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/040/types.go b/vendor/github.com/containernetworking/cni/pkg/types/040/types.go deleted file mode 100644 index 3633b0eaa3..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/types/040/types.go +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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 -// -// http://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. - -package types040 - -import ( - "encoding/json" - "fmt" - "io" - "net" - "os" - - "github.com/containernetworking/cni/pkg/types" - types020 "github.com/containernetworking/cni/pkg/types/020" - convert "github.com/containernetworking/cni/pkg/types/internal" -) - -const ImplementedSpecVersion string = "0.4.0" - -var supportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion} - -// Register converters for all versions less than the implemented spec version -func init() { - // Up-converters - convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x) - convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x) - convert.RegisterConverter("0.3.0", supportedVersions, convertInternal) - convert.RegisterConverter("0.3.1", supportedVersions, convertInternal) - - // Down-converters - convert.RegisterConverter("0.4.0", []string{"0.3.0", "0.3.1"}, convertInternal) - convert.RegisterConverter("0.4.0", []string{"0.1.0", "0.2.0"}, convertTo02x) - convert.RegisterConverter("0.3.1", []string{"0.1.0", "0.2.0"}, convertTo02x) - convert.RegisterConverter("0.3.0", []string{"0.1.0", "0.2.0"}, convertTo02x) - - // Creator - convert.RegisterCreator(supportedVersions, NewResult) -} - -func NewResult(data []byte) (types.Result, error) { - result := &Result{} - if err := json.Unmarshal(data, result); err != nil { - return nil, err - } - for _, v := range supportedVersions { - if result.CNIVersion == v { - return result, nil - } - } - return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q", - supportedVersions, result.CNIVersion) -} - -func GetResult(r types.Result) (*Result, error) { - resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) - if err != nil { - return nil, err - } - result, ok := resultCurrent.(*Result) - if !ok { - return nil, fmt.Errorf("failed to convert result") - } - return result, nil -} - -func NewResultFromResult(result types.Result) (*Result, error) { - newResult, err := convert.Convert(result, ImplementedSpecVersion) - if err != nil { - return nil, err - } - return newResult.(*Result), nil -} - -// Result is what gets returned from the plugin (via stdout) to the caller -type Result struct { - CNIVersion string `json:"cniVersion,omitempty"` - Interfaces []*Interface `json:"interfaces,omitempty"` - IPs []*IPConfig `json:"ips,omitempty"` - Routes []*types.Route `json:"routes,omitempty"` - DNS types.DNS `json:"dns,omitempty"` -} - -func convert020IPConfig(from *types020.IPConfig, ipVersion string) *IPConfig { - return &IPConfig{ - Version: ipVersion, - Address: from.IP, - Gateway: from.Gateway, - } -} - -func convertFrom02x(from types.Result, toVersion string) (types.Result, error) { - fromResult := from.(*types020.Result) - toResult := &Result{ - CNIVersion: toVersion, - DNS: *fromResult.DNS.Copy(), - Routes: []*types.Route{}, - } - if fromResult.IP4 != nil { - toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP4, "4")) - for _, fromRoute := range fromResult.IP4.Routes { - toResult.Routes = append(toResult.Routes, fromRoute.Copy()) - } - } - - if fromResult.IP6 != nil { - toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP6, "6")) - for _, fromRoute := range fromResult.IP6.Routes { - toResult.Routes = append(toResult.Routes, fromRoute.Copy()) - } - } - - return toResult, nil -} - -func convertInternal(from types.Result, toVersion string) (types.Result, error) { - fromResult := from.(*Result) - toResult := &Result{ - CNIVersion: toVersion, - DNS: *fromResult.DNS.Copy(), - Routes: []*types.Route{}, - } - for _, fromIntf := range fromResult.Interfaces { - toResult.Interfaces = append(toResult.Interfaces, fromIntf.Copy()) - } - for _, fromIPC := range fromResult.IPs { - toResult.IPs = append(toResult.IPs, fromIPC.Copy()) - } - for _, fromRoute := range fromResult.Routes { - toResult.Routes = append(toResult.Routes, fromRoute.Copy()) - } - return toResult, nil -} - -func convertTo02x(from types.Result, toVersion string) (types.Result, error) { - fromResult := from.(*Result) - toResult := &types020.Result{ - CNIVersion: toVersion, - DNS: *fromResult.DNS.Copy(), - } - - for _, fromIP := range fromResult.IPs { - // Only convert the first IP address of each version as 0.2.0 - // and earlier cannot handle multiple IP addresses - if fromIP.Version == "4" && toResult.IP4 == nil { - toResult.IP4 = &types020.IPConfig{ - IP: fromIP.Address, - Gateway: fromIP.Gateway, - } - } else if fromIP.Version == "6" && toResult.IP6 == nil { - toResult.IP6 = &types020.IPConfig{ - IP: fromIP.Address, - Gateway: fromIP.Gateway, - } - } - if toResult.IP4 != nil && toResult.IP6 != nil { - break - } - } - - for _, fromRoute := range fromResult.Routes { - is4 := fromRoute.Dst.IP.To4() != nil - if is4 && toResult.IP4 != nil { - toResult.IP4.Routes = append(toResult.IP4.Routes, types.Route{ - Dst: fromRoute.Dst, - GW: fromRoute.GW, - }) - } else if !is4 && toResult.IP6 != nil { - toResult.IP6.Routes = append(toResult.IP6.Routes, types.Route{ - Dst: fromRoute.Dst, - GW: fromRoute.GW, - }) - } - } - - // 0.2.0 and earlier require at least one IP address in the Result - if toResult.IP4 == nil && toResult.IP6 == nil { - return nil, fmt.Errorf("cannot convert: no valid IP addresses") - } - - return toResult, nil -} - -func (r *Result) Version() string { - return r.CNIVersion -} - -func (r *Result) GetAsVersion(version string) (types.Result, error) { - // If the creator of the result did not set the CNIVersion, assume it - // should be the highest spec version implemented by this Result - if r.CNIVersion == "" { - r.CNIVersion = ImplementedSpecVersion - } - return convert.Convert(r, version) -} - -func (r *Result) Print() error { - return r.PrintTo(os.Stdout) -} - -func (r *Result) PrintTo(writer io.Writer) error { - data, err := json.MarshalIndent(r, "", " ") - if err != nil { - return err - } - _, err = writer.Write(data) - return err -} - -// Interface contains values about the created interfaces -type Interface struct { - Name string `json:"name"` - Mac string `json:"mac,omitempty"` - Sandbox string `json:"sandbox,omitempty"` -} - -func (i *Interface) String() string { - return fmt.Sprintf("%+v", *i) -} - -func (i *Interface) Copy() *Interface { - if i == nil { - return nil - } - newIntf := *i - return &newIntf -} - -// Int returns a pointer to the int value passed in. Used to -// set the IPConfig.Interface field. -func Int(v int) *int { - return &v -} - -// IPConfig contains values necessary to configure an IP address on an interface -type IPConfig struct { - // IP version, either "4" or "6" - Version string - // Index into Result structs Interfaces list - Interface *int - Address net.IPNet - Gateway net.IP -} - -func (i *IPConfig) String() string { - return fmt.Sprintf("%+v", *i) -} - -func (i *IPConfig) Copy() *IPConfig { - if i == nil { - return nil - } - - ipc := &IPConfig{ - Version: i.Version, - Address: i.Address, - Gateway: i.Gateway, - } - if i.Interface != nil { - intf := *i.Interface - ipc.Interface = &intf - } - return ipc -} - -// JSON (un)marshallable types -type ipConfig struct { - Version string `json:"version"` - Interface *int `json:"interface,omitempty"` - Address types.IPNet `json:"address"` - Gateway net.IP `json:"gateway,omitempty"` -} - -func (c *IPConfig) MarshalJSON() ([]byte, error) { - ipc := ipConfig{ - Version: c.Version, - Interface: c.Interface, - Address: types.IPNet(c.Address), - Gateway: c.Gateway, - } - - return json.Marshal(ipc) -} - -func (c *IPConfig) UnmarshalJSON(data []byte) error { - ipc := ipConfig{} - if err := json.Unmarshal(data, &ipc); err != nil { - return err - } - - c.Version = ipc.Version - c.Interface = ipc.Interface - c.Address = net.IPNet(ipc.Address) - c.Gateway = ipc.Gateway - return nil -} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/100/types.go b/vendor/github.com/containernetworking/cni/pkg/types/100/types.go deleted file mode 100644 index f58b91206d..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/types/100/types.go +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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 -// -// http://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. - -package types100 - -import ( - "encoding/json" - "fmt" - "io" - "net" - "os" - - "github.com/containernetworking/cni/pkg/types" - types040 "github.com/containernetworking/cni/pkg/types/040" - convert "github.com/containernetworking/cni/pkg/types/internal" -) - -// The types did not change between v1.0 and v1.1 -const ImplementedSpecVersion string = "1.1.0" - -var supportedVersions = []string{"1.0.0", "1.1.0"} - -// Register converters for all versions less than the implemented spec version -func init() { - // Up-converters - convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x) - convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x) - convert.RegisterConverter("0.3.0", supportedVersions, convertFrom04x) - convert.RegisterConverter("0.3.1", supportedVersions, convertFrom04x) - convert.RegisterConverter("0.4.0", supportedVersions, convertFrom04x) - convert.RegisterConverter("1.0.0", []string{"1.1.0"}, convertFrom100) - - // Down-converters - convert.RegisterConverter("1.0.0", []string{"0.3.0", "0.3.1", "0.4.0"}, convertTo04x) - convert.RegisterConverter("1.0.0", []string{"0.1.0", "0.2.0"}, convertTo02x) - convert.RegisterConverter("1.1.0", []string{"0.3.0", "0.3.1", "0.4.0"}, convertTo04x) - convert.RegisterConverter("1.1.0", []string{"0.1.0", "0.2.0"}, convertTo02x) - convert.RegisterConverter("1.1.0", []string{"1.0.0"}, convertFrom100) - - // Creator - convert.RegisterCreator(supportedVersions, NewResult) -} - -func NewResult(data []byte) (types.Result, error) { - result := &Result{} - if err := json.Unmarshal(data, result); err != nil { - return nil, err - } - for _, v := range supportedVersions { - if result.CNIVersion == v { - return result, nil - } - } - return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q", - supportedVersions, result.CNIVersion) -} - -func GetResult(r types.Result) (*Result, error) { - resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) - if err != nil { - return nil, err - } - result, ok := resultCurrent.(*Result) - if !ok { - return nil, fmt.Errorf("failed to convert result") - } - return result, nil -} - -func NewResultFromResult(result types.Result) (*Result, error) { - newResult, err := convert.Convert(result, ImplementedSpecVersion) - if err != nil { - return nil, err - } - return newResult.(*Result), nil -} - -// Result is what gets returned from the plugin (via stdout) to the caller -type Result struct { - CNIVersion string `json:"cniVersion,omitempty"` - Interfaces []*Interface `json:"interfaces,omitempty"` - IPs []*IPConfig `json:"ips,omitempty"` - Routes []*types.Route `json:"routes,omitempty"` - DNS types.DNS `json:"dns,omitempty"` -} - -// Note: DNS should be omit if DNS is empty but default Marshal function -// will output empty structure hence need to write a Marshal function -func (r *Result) MarshalJSON() ([]byte, error) { - // use type alias to escape recursion for json.Marshal() to MarshalJSON() - type fixObjType = Result - - bytes, err := json.Marshal(fixObjType(*r)) //nolint:all - if err != nil { - return nil, err - } - - fixupObj := make(map[string]interface{}) - if err := json.Unmarshal(bytes, &fixupObj); err != nil { - return nil, err - } - - if r.DNS.IsEmpty() { - delete(fixupObj, "dns") - } - - return json.Marshal(fixupObj) -} - -// convertFrom100 does nothing except set the version; the types are the same -func convertFrom100(from types.Result, toVersion string) (types.Result, error) { - fromResult := from.(*Result) - - result := &Result{ - CNIVersion: toVersion, - Interfaces: fromResult.Interfaces, - IPs: fromResult.IPs, - Routes: fromResult.Routes, - DNS: fromResult.DNS, - } - return result, nil -} - -func convertFrom02x(from types.Result, toVersion string) (types.Result, error) { - result040, err := convert.Convert(from, "0.4.0") - if err != nil { - return nil, err - } - result100, err := convertFrom04x(result040, toVersion) - if err != nil { - return nil, err - } - return result100, nil -} - -func convertIPConfigFrom040(from *types040.IPConfig) *IPConfig { - to := &IPConfig{ - Address: from.Address, - Gateway: from.Gateway, - } - if from.Interface != nil { - intf := *from.Interface - to.Interface = &intf - } - return to -} - -func convertInterfaceFrom040(from *types040.Interface) *Interface { - return &Interface{ - Name: from.Name, - Mac: from.Mac, - Sandbox: from.Sandbox, - } -} - -func convertFrom04x(from types.Result, toVersion string) (types.Result, error) { - fromResult := from.(*types040.Result) - toResult := &Result{ - CNIVersion: toVersion, - DNS: *fromResult.DNS.Copy(), - Routes: []*types.Route{}, - } - for _, fromIntf := range fromResult.Interfaces { - toResult.Interfaces = append(toResult.Interfaces, convertInterfaceFrom040(fromIntf)) - } - for _, fromIPC := range fromResult.IPs { - toResult.IPs = append(toResult.IPs, convertIPConfigFrom040(fromIPC)) - } - for _, fromRoute := range fromResult.Routes { - toResult.Routes = append(toResult.Routes, fromRoute.Copy()) - } - return toResult, nil -} - -func convertIPConfigTo040(from *IPConfig) *types040.IPConfig { - version := "6" - if from.Address.IP.To4() != nil { - version = "4" - } - to := &types040.IPConfig{ - Version: version, - Address: from.Address, - Gateway: from.Gateway, - } - if from.Interface != nil { - intf := *from.Interface - to.Interface = &intf - } - return to -} - -func convertInterfaceTo040(from *Interface) *types040.Interface { - return &types040.Interface{ - Name: from.Name, - Mac: from.Mac, - Sandbox: from.Sandbox, - } -} - -func convertTo04x(from types.Result, toVersion string) (types.Result, error) { - fromResult := from.(*Result) - toResult := &types040.Result{ - CNIVersion: toVersion, - DNS: *fromResult.DNS.Copy(), - Routes: []*types.Route{}, - } - for _, fromIntf := range fromResult.Interfaces { - toResult.Interfaces = append(toResult.Interfaces, convertInterfaceTo040(fromIntf)) - } - for _, fromIPC := range fromResult.IPs { - toResult.IPs = append(toResult.IPs, convertIPConfigTo040(fromIPC)) - } - for _, fromRoute := range fromResult.Routes { - toResult.Routes = append(toResult.Routes, fromRoute.Copy()) - } - return toResult, nil -} - -func convertTo02x(from types.Result, toVersion string) (types.Result, error) { - // First convert to 0.4.0 - result040, err := convertTo04x(from, "0.4.0") - if err != nil { - return nil, err - } - result02x, err := convert.Convert(result040, toVersion) - if err != nil { - return nil, err - } - return result02x, nil -} - -func (r *Result) Version() string { - return r.CNIVersion -} - -func (r *Result) GetAsVersion(version string) (types.Result, error) { - // If the creator of the result did not set the CNIVersion, assume it - // should be the highest spec version implemented by this Result - if r.CNIVersion == "" { - r.CNIVersion = ImplementedSpecVersion - } - return convert.Convert(r, version) -} - -func (r *Result) Print() error { - return r.PrintTo(os.Stdout) -} - -func (r *Result) PrintTo(writer io.Writer) error { - data, err := json.MarshalIndent(r, "", " ") - if err != nil { - return err - } - _, err = writer.Write(data) - return err -} - -// Interface contains values about the created interfaces -type Interface struct { - Name string `json:"name"` - Mac string `json:"mac,omitempty"` - Mtu int `json:"mtu,omitempty"` - Sandbox string `json:"sandbox,omitempty"` - SocketPath string `json:"socketPath,omitempty"` - PciID string `json:"pciID,omitempty"` -} - -func (i *Interface) String() string { - return fmt.Sprintf("%+v", *i) -} - -func (i *Interface) Copy() *Interface { - if i == nil { - return nil - } - newIntf := *i - return &newIntf -} - -// Int returns a pointer to the int value passed in. Used to -// set the IPConfig.Interface field. -func Int(v int) *int { - return &v -} - -// IPConfig contains values necessary to configure an IP address on an interface -type IPConfig struct { - // Index into Result structs Interfaces list - Interface *int - Address net.IPNet - Gateway net.IP -} - -func (i *IPConfig) String() string { - return fmt.Sprintf("%+v", *i) -} - -func (i *IPConfig) Copy() *IPConfig { - if i == nil { - return nil - } - - ipc := &IPConfig{ - Address: i.Address, - Gateway: i.Gateway, - } - if i.Interface != nil { - intf := *i.Interface - ipc.Interface = &intf - } - return ipc -} - -// JSON (un)marshallable types -type ipConfig struct { - Interface *int `json:"interface,omitempty"` - Address types.IPNet `json:"address"` - Gateway net.IP `json:"gateway,omitempty"` -} - -func (c *IPConfig) MarshalJSON() ([]byte, error) { - ipc := ipConfig{ - Interface: c.Interface, - Address: types.IPNet(c.Address), - Gateway: c.Gateway, - } - - return json.Marshal(ipc) -} - -func (c *IPConfig) UnmarshalJSON(data []byte) error { - ipc := ipConfig{} - if err := json.Unmarshal(data, &ipc); err != nil { - return err - } - - c.Interface = ipc.Interface - c.Address = net.IPNet(ipc.Address) - c.Gateway = ipc.Gateway - return nil -} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/args.go b/vendor/github.com/containernetworking/cni/pkg/types/args.go deleted file mode 100644 index 68a602bfdb..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/types/args.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2015 CNI authors -// -// 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 -// -// http://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. - -package types - -import ( - "encoding" - "fmt" - "reflect" - "strings" -) - -// UnmarshallableBool typedef for builtin bool -// because builtin type's methods can't be declared -type UnmarshallableBool bool - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -// Returns boolean true if the string is "1" or "true" or "True" -// Returns boolean false if the string is "0" or "false" or "Falseā€ -func (b *UnmarshallableBool) UnmarshalText(data []byte) error { - s := strings.ToLower(string(data)) - switch s { - case "1", "true": - *b = true - case "0", "false": - *b = false - default: - return fmt.Errorf("boolean unmarshal error: invalid input %s", s) - } - return nil -} - -// UnmarshallableString typedef for builtin string -type UnmarshallableString string - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -// Returns the string -func (s *UnmarshallableString) UnmarshalText(data []byte) error { - *s = UnmarshallableString(data) - return nil -} - -// CommonArgs contains the IgnoreUnknown argument -// and must be embedded by all Arg structs -type CommonArgs struct { - IgnoreUnknown UnmarshallableBool `json:"ignoreunknown,omitempty"` -} - -// GetKeyField is a helper function to receive Values -// Values that represent a pointer to a struct -func GetKeyField(keyString string, v reflect.Value) reflect.Value { - return v.Elem().FieldByName(keyString) -} - -// UnmarshalableArgsError is used to indicate error unmarshalling args -// from the args-string in the form "K=V;K2=V2;..." -type UnmarshalableArgsError struct { - error -} - -// LoadArgs parses args from a string in the form "K=V;K2=V2;..." -func LoadArgs(args string, container interface{}) error { - if args == "" { - return nil - } - - containerValue := reflect.ValueOf(container) - - pairs := strings.Split(args, ";") - unknownArgs := []string{} - for _, pair := range pairs { - kv := strings.Split(pair, "=") - if len(kv) != 2 { - return fmt.Errorf("ARGS: invalid pair %q", pair) - } - keyString := kv[0] - valueString := kv[1] - keyField := GetKeyField(keyString, containerValue) - if !keyField.IsValid() { - unknownArgs = append(unknownArgs, pair) - continue - } - - var keyFieldInterface interface{} - switch { - case keyField.Kind() == reflect.Ptr: - keyField.Set(reflect.New(keyField.Type().Elem())) - keyFieldInterface = keyField.Interface() - case keyField.CanAddr() && keyField.Addr().CanInterface(): - keyFieldInterface = keyField.Addr().Interface() - default: - return UnmarshalableArgsError{fmt.Errorf("field '%s' has no valid interface", keyString)} - } - u, ok := keyFieldInterface.(encoding.TextUnmarshaler) - if !ok { - return UnmarshalableArgsError{fmt.Errorf( - "ARGS: cannot unmarshal into field '%s' - type '%s' does not implement encoding.TextUnmarshaler", - keyString, reflect.TypeOf(keyFieldInterface))} - } - err := u.UnmarshalText([]byte(valueString)) - if err != nil { - return fmt.Errorf("ARGS: error parsing value of pair %q: %w", pair, err) - } - } - - isIgnoreUnknown := GetKeyField("IgnoreUnknown", containerValue).Bool() - if len(unknownArgs) > 0 && !isIgnoreUnknown { - return fmt.Errorf("ARGS: unknown args %q", unknownArgs) - } - return nil -} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/create/create.go b/vendor/github.com/containernetworking/cni/pkg/types/create/create.go deleted file mode 100644 index 452cb62201..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/types/create/create.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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 -// -// http://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. - -package create - -import ( - "encoding/json" - "fmt" - - "github.com/containernetworking/cni/pkg/types" - _ "github.com/containernetworking/cni/pkg/types/020" - _ "github.com/containernetworking/cni/pkg/types/040" - _ "github.com/containernetworking/cni/pkg/types/100" - convert "github.com/containernetworking/cni/pkg/types/internal" -) - -// DecodeVersion returns the CNI version from CNI configuration or result JSON, -// or an error if the operation could not be performed. -func DecodeVersion(jsonBytes []byte) (string, error) { - var conf struct { - CNIVersion string `json:"cniVersion"` - } - err := json.Unmarshal(jsonBytes, &conf) - if err != nil { - return "", fmt.Errorf("decoding version from network config: %w", err) - } - if conf.CNIVersion == "" { - return "0.1.0", nil - } - return conf.CNIVersion, nil -} - -// Create creates a CNI Result using the given JSON with the expected -// version, or an error if the creation could not be performed -func Create(version string, bytes []byte) (types.Result, error) { - return convert.Create(version, bytes) -} - -// CreateFromBytes creates a CNI Result from the given JSON, automatically -// detecting the CNI spec version of the result. An error is returned if the -// operation could not be performed. -func CreateFromBytes(bytes []byte) (types.Result, error) { - version, err := DecodeVersion(bytes) - if err != nil { - return nil, err - } - return convert.Create(version, bytes) -} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go b/vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go deleted file mode 100644 index bdbe4b0a59..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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 -// -// http://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. - -package convert - -import ( - "fmt" - - "github.com/containernetworking/cni/pkg/types" -) - -// ConvertFn should convert from the given arbitrary Result type into a -// Result implementing CNI specification version passed in toVersion. -// The function is guaranteed to be passed a Result type matching the -// fromVersion it was registered with, and is guaranteed to be -// passed a toVersion matching one of the toVersions it was registered with. -type ConvertFn func(from types.Result, toVersion string) (types.Result, error) - -type converter struct { - // fromVersion is the CNI Result spec version that convertFn accepts - fromVersion string - // toVersions is a list of versions that convertFn can convert to - toVersions []string - convertFn ConvertFn -} - -var converters []*converter - -func findConverter(fromVersion, toVersion string) *converter { - for _, c := range converters { - if c.fromVersion == fromVersion { - for _, v := range c.toVersions { - if v == toVersion { - return c - } - } - } - } - return nil -} - -// Convert converts a CNI Result to the requested CNI specification version, -// or returns an error if the conversion could not be performed or failed -func Convert(from types.Result, toVersion string) (types.Result, error) { - if toVersion == "" { - toVersion = "0.1.0" - } - - fromVersion := from.Version() - - // Shortcut for same version - if fromVersion == toVersion { - return from, nil - } - - // Otherwise find the right converter - c := findConverter(fromVersion, toVersion) - if c == nil { - return nil, fmt.Errorf("no converter for CNI result version %s to %s", - fromVersion, toVersion) - } - return c.convertFn(from, toVersion) -} - -// RegisterConverter registers a CNI Result converter. SHOULD NOT BE CALLED -// EXCEPT FROM CNI ITSELF. -func RegisterConverter(fromVersion string, toVersions []string, convertFn ConvertFn) { - // Make sure there is no converter already registered for these - // from and to versions - for _, v := range toVersions { - if findConverter(fromVersion, v) != nil { - panic(fmt.Sprintf("converter already registered for %s to %s", - fromVersion, v)) - } - } - converters = append(converters, &converter{ - fromVersion: fromVersion, - toVersions: toVersions, - convertFn: convertFn, - }) -} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/internal/create.go b/vendor/github.com/containernetworking/cni/pkg/types/internal/create.go deleted file mode 100644 index 9636309125..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/types/internal/create.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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 -// -// http://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. - -package convert - -import ( - "fmt" - - "github.com/containernetworking/cni/pkg/types" -) - -type ResultFactoryFunc func([]byte) (types.Result, error) - -type creator struct { - // CNI Result spec versions that createFn can create a Result for - versions []string - createFn ResultFactoryFunc -} - -var creators []*creator - -func findCreator(version string) *creator { - for _, c := range creators { - for _, v := range c.versions { - if v == version { - return c - } - } - } - return nil -} - -// Create creates a CNI Result using the given JSON, or an error if the creation -// could not be performed -func Create(version string, bytes []byte) (types.Result, error) { - if c := findCreator(version); c != nil { - return c.createFn(bytes) - } - return nil, fmt.Errorf("unsupported CNI result version %q", version) -} - -// RegisterCreator registers a CNI Result creator. SHOULD NOT BE CALLED -// EXCEPT FROM CNI ITSELF. -func RegisterCreator(versions []string, createFn ResultFactoryFunc) { - // Make sure there is no creator already registered for these versions - for _, v := range versions { - if findCreator(v) != nil { - panic(fmt.Sprintf("creator already registered for %s", v)) - } - } - creators = append(creators, &creator{ - versions: versions, - createFn: createFn, - }) -} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/types.go b/vendor/github.com/containernetworking/cni/pkg/types/types.go deleted file mode 100644 index f4b3ce3535..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/types/types.go +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright 2015 CNI authors -// -// 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 -// -// http://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. - -package types - -import ( - "encoding/json" - "fmt" - "io" - "net" - "os" -) - -// like net.IPNet but adds JSON marshalling and unmarshalling -type IPNet net.IPNet - -// ParseCIDR takes a string like "10.2.3.1/24" and -// return IPNet with "10.2.3.1" and /24 mask -func ParseCIDR(s string) (*net.IPNet, error) { - ip, ipn, err := net.ParseCIDR(s) - if err != nil { - return nil, err - } - - ipn.IP = ip - return ipn, nil -} - -func (n IPNet) MarshalJSON() ([]byte, error) { - return json.Marshal((*net.IPNet)(&n).String()) -} - -func (n *IPNet) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err != nil { - return err - } - - tmp, err := ParseCIDR(s) - if err != nil { - return err - } - - *n = IPNet(*tmp) - return nil -} - -// Use PluginConf instead of NetConf, the NetConf -// backwards-compat alias will be removed in a future release. -type NetConf = PluginConf - -// PluginConf describes a plugin configuration for a specific network. -type PluginConf struct { - CNIVersion string `json:"cniVersion,omitempty"` - - Name string `json:"name,omitempty"` - Type string `json:"type,omitempty"` - Capabilities map[string]bool `json:"capabilities,omitempty"` - IPAM IPAM `json:"ipam,omitempty"` - DNS DNS `json:"dns,omitempty"` - - RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` - PrevResult Result `json:"-"` - - // ValidAttachments is only supplied when executing a GC operation - ValidAttachments []GCAttachment `json:"cni.dev/valid-attachments,omitempty"` -} - -// GCAttachment is the parameters to a GC call -- namely, -// the container ID and ifname pair that represents a -// still-valid attachment. -type GCAttachment struct { - ContainerID string `json:"containerID"` - IfName string `json:"ifname"` -} - -// Note: DNS should be omit if DNS is empty but default Marshal function -// will output empty structure hence need to write a Marshal function -func (n *PluginConf) MarshalJSON() ([]byte, error) { - bytes, err := json.Marshal(*n) - if err != nil { - return nil, err - } - - fixupObj := make(map[string]interface{}) - if err := json.Unmarshal(bytes, &fixupObj); err != nil { - return nil, err - } - - if n.DNS.IsEmpty() { - delete(fixupObj, "dns") - } - - return json.Marshal(fixupObj) -} - -type IPAM struct { - Type string `json:"type,omitempty"` -} - -// IsEmpty returns true if IPAM structure has no value, otherwise return false -func (i *IPAM) IsEmpty() bool { - return i.Type == "" -} - -// NetConfList describes an ordered list of networks. -type NetConfList struct { - CNIVersion string `json:"cniVersion,omitempty"` - - Name string `json:"name,omitempty"` - DisableCheck bool `json:"disableCheck,omitempty"` - DisableGC bool `json:"disableGC,omitempty"` - Plugins []*PluginConf `json:"plugins,omitempty"` -} - -// Result is an interface that provides the result of plugin execution -type Result interface { - // The highest CNI specification result version the result supports - // without having to convert - Version() string - - // Returns the result converted into the requested CNI specification - // result version, or an error if conversion failed - GetAsVersion(version string) (Result, error) - - // Prints the result in JSON format to stdout - Print() error - - // Prints the result in JSON format to provided writer - PrintTo(writer io.Writer) error -} - -func PrintResult(result Result, version string) error { - newResult, err := result.GetAsVersion(version) - if err != nil { - return err - } - return newResult.Print() -} - -// DNS contains values interesting for DNS resolvers -type DNS struct { - Nameservers []string `json:"nameservers,omitempty"` - Domain string `json:"domain,omitempty"` - Search []string `json:"search,omitempty"` - Options []string `json:"options,omitempty"` -} - -// IsEmpty returns true if DNS structure has no value, otherwise return false -func (d *DNS) IsEmpty() bool { - if len(d.Nameservers) == 0 && d.Domain == "" && len(d.Search) == 0 && len(d.Options) == 0 { - return true - } - return false -} - -func (d *DNS) Copy() *DNS { - if d == nil { - return nil - } - - to := &DNS{Domain: d.Domain} - to.Nameservers = append(to.Nameservers, d.Nameservers...) - to.Search = append(to.Search, d.Search...) - to.Options = append(to.Options, d.Options...) - return to -} - -type Route struct { - Dst net.IPNet - GW net.IP - MTU int - AdvMSS int - Priority int - Table *int - Scope *int -} - -func (r *Route) String() string { - table := "" - if r.Table != nil { - table = fmt.Sprintf("%d", *r.Table) - } - - scope := "" - if r.Scope != nil { - scope = fmt.Sprintf("%d", *r.Scope) - } - - return fmt.Sprintf("{Dst:%+v GW:%v MTU:%d AdvMSS:%d Priority:%d Table:%s Scope:%s}", r.Dst, r.GW, r.MTU, r.AdvMSS, r.Priority, table, scope) -} - -func (r *Route) Copy() *Route { - if r == nil { - return nil - } - - route := &Route{ - Dst: r.Dst, - GW: r.GW, - MTU: r.MTU, - AdvMSS: r.AdvMSS, - Priority: r.Priority, - Scope: r.Scope, - } - - if r.Table != nil { - table := *r.Table - route.Table = &table - } - - if r.Scope != nil { - scope := *r.Scope - route.Scope = &scope - } - - return route -} - -// Well known error codes -// see https://github.com/containernetworking/cni/blob/main/SPEC.md#well-known-error-codes -const ( - ErrUnknown uint = iota // 0 - ErrIncompatibleCNIVersion // 1 - ErrUnsupportedField // 2 - ErrUnknownContainer // 3 - ErrInvalidEnvironmentVariables // 4 - ErrIOFailure // 5 - ErrDecodingFailure // 6 - ErrInvalidNetworkConfig // 7 - ErrInvalidNetNS // 8 - ErrTryAgainLater uint = 11 - ErrInternal uint = 999 -) - -type Error struct { - Code uint `json:"code"` - Msg string `json:"msg"` - Details string `json:"details,omitempty"` -} - -func NewError(code uint, msg, details string) *Error { - return &Error{ - Code: code, - Msg: msg, - Details: details, - } -} - -func (e *Error) Error() string { - details := "" - if e.Details != "" { - details = fmt.Sprintf("; %v", e.Details) - } - return fmt.Sprintf("%v%v", e.Msg, details) -} - -func (e *Error) Print() error { - return prettyPrint(e) -} - -// net.IPNet is not JSON (un)marshallable so this duality is needed -// for our custom IPNet type - -// JSON (un)marshallable types -type route struct { - Dst IPNet `json:"dst"` - GW net.IP `json:"gw,omitempty"` - MTU int `json:"mtu,omitempty"` - AdvMSS int `json:"advmss,omitempty"` - Priority int `json:"priority,omitempty"` - Table *int `json:"table,omitempty"` - Scope *int `json:"scope,omitempty"` -} - -func (r *Route) UnmarshalJSON(data []byte) error { - rt := route{} - if err := json.Unmarshal(data, &rt); err != nil { - return err - } - - r.Dst = net.IPNet(rt.Dst) - r.GW = rt.GW - r.MTU = rt.MTU - r.AdvMSS = rt.AdvMSS - r.Priority = rt.Priority - r.Table = rt.Table - r.Scope = rt.Scope - - return nil -} - -func (r Route) MarshalJSON() ([]byte, error) { - rt := route{ - Dst: IPNet(r.Dst), - GW: r.GW, - MTU: r.MTU, - AdvMSS: r.AdvMSS, - Priority: r.Priority, - Table: r.Table, - Scope: r.Scope, - } - - return json.Marshal(rt) -} - -func prettyPrint(obj interface{}) error { - data, err := json.MarshalIndent(obj, "", " ") - if err != nil { - return err - } - _, err = os.Stdout.Write(data) - return err -} diff --git a/vendor/github.com/containernetworking/cni/pkg/utils/utils.go b/vendor/github.com/containernetworking/cni/pkg/utils/utils.go deleted file mode 100644 index 1981d25569..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/utils/utils.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2019 CNI authors -// -// 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 -// -// http://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. - -package utils - -import ( - "bytes" - "fmt" - "regexp" - "unicode" - - "github.com/containernetworking/cni/pkg/types" -) - -const ( - // cniValidNameChars is the regexp used to validate valid characters in - // containerID and networkName - cniValidNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.\-]` - - // maxInterfaceNameLength is the length max of a valid interface name - maxInterfaceNameLength = 15 -) - -var cniReg = regexp.MustCompile(`^` + cniValidNameChars + `*$`) - -// ValidateContainerID will validate that the supplied containerID is not empty does not contain invalid characters -func ValidateContainerID(containerID string) *types.Error { - if containerID == "" { - return types.NewError(types.ErrUnknownContainer, "missing containerID", "") - } - if !cniReg.MatchString(containerID) { - return types.NewError(types.ErrInvalidEnvironmentVariables, "invalid characters in containerID", containerID) - } - return nil -} - -// ValidateNetworkName will validate that the supplied networkName does not contain invalid characters -func ValidateNetworkName(networkName string) *types.Error { - if networkName == "" { - return types.NewError(types.ErrInvalidNetworkConfig, "missing network name:", "") - } - if !cniReg.MatchString(networkName) { - return types.NewError(types.ErrInvalidNetworkConfig, "invalid characters found in network name", networkName) - } - return nil -} - -// ValidateInterfaceName will validate the interface name based on the four rules below -// 1. The name must not be empty -// 2. The name must be less than 16 characters -// 3. The name must not be "." or ".." -// 4. The name must not contain / or : or any whitespace characters -// ref to https://github.com/torvalds/linux/blob/master/net/core/dev.c#L1024 -func ValidateInterfaceName(ifName string) *types.Error { - if len(ifName) == 0 { - return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is empty", "") - } - if len(ifName) > maxInterfaceNameLength { - return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is too long", fmt.Sprintf("interface name should be less than %d characters", maxInterfaceNameLength+1)) - } - if ifName == "." || ifName == ".." { - return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is . or ..", "") - } - for _, r := range bytes.Runes([]byte(ifName)) { - if r == '/' || r == ':' || unicode.IsSpace(r) { - return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name contains / or : or whitespace characters", "") - } - } - - return nil -} diff --git a/vendor/github.com/containernetworking/cni/pkg/version/conf.go b/vendor/github.com/containernetworking/cni/pkg/version/conf.go deleted file mode 100644 index 808c33b838..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/version/conf.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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 -// -// http://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. - -package version - -import ( - "github.com/containernetworking/cni/pkg/types/create" -) - -// ConfigDecoder can decode the CNI version available in network config data -type ConfigDecoder struct{} - -func (*ConfigDecoder) Decode(jsonBytes []byte) (string, error) { - return create.DecodeVersion(jsonBytes) -} diff --git a/vendor/github.com/containernetworking/cni/pkg/version/plugin.go b/vendor/github.com/containernetworking/cni/pkg/version/plugin.go deleted file mode 100644 index e3bd375bca..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/version/plugin.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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 -// -// http://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. - -package version - -import ( - "encoding/json" - "fmt" - "io" - "strconv" - "strings" -) - -// PluginInfo reports information about CNI versioning -type PluginInfo interface { - // SupportedVersions returns one or more CNI spec versions that the plugin - // supports. If input is provided in one of these versions, then the plugin - // promises to use the same CNI version in its response - SupportedVersions() []string - - // Encode writes this CNI version information as JSON to the given Writer - Encode(io.Writer) error -} - -type pluginInfo struct { - CNIVersion_ string `json:"cniVersion"` - SupportedVersions_ []string `json:"supportedVersions,omitempty"` -} - -// pluginInfo implements the PluginInfo interface -var _ PluginInfo = &pluginInfo{} - -func (p *pluginInfo) Encode(w io.Writer) error { - return json.NewEncoder(w).Encode(p) -} - -func (p *pluginInfo) SupportedVersions() []string { - return p.SupportedVersions_ -} - -// PluginSupports returns a new PluginInfo that will report the given versions -// as supported -func PluginSupports(supportedVersions ...string) PluginInfo { - if len(supportedVersions) < 1 { - panic("programmer error: you must support at least one version") - } - return &pluginInfo{ - CNIVersion_: Current(), - SupportedVersions_: supportedVersions, - } -} - -// PluginDecoder can decode the response returned by a plugin's VERSION command -type PluginDecoder struct{} - -func (*PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) { - var info pluginInfo - err := json.Unmarshal(jsonBytes, &info) - if err != nil { - return nil, fmt.Errorf("decoding version info: %w", err) - } - if info.CNIVersion_ == "" { - return nil, fmt.Errorf("decoding version info: missing field cniVersion") - } - if len(info.SupportedVersions_) == 0 { - if info.CNIVersion_ == "0.2.0" { - return PluginSupports("0.1.0", "0.2.0"), nil - } - return nil, fmt.Errorf("decoding version info: missing field supportedVersions") - } - return &info, nil -} - -// ParseVersion parses a version string like "3.0.1" or "0.4.5" into major, -// minor, and micro numbers or returns an error -func ParseVersion(version string) (int, int, int, error) { - var major, minor, micro int - if version == "" { // special case: no version declared == v0.1.0 - return 0, 1, 0, nil - } - - parts := strings.Split(version, ".") - if len(parts) >= 4 { - return -1, -1, -1, fmt.Errorf("invalid version %q: too many parts", version) - } - - major, err := strconv.Atoi(parts[0]) - if err != nil { - return -1, -1, -1, fmt.Errorf("failed to convert major version part %q: %w", parts[0], err) - } - - if len(parts) >= 2 { - minor, err = strconv.Atoi(parts[1]) - if err != nil { - return -1, -1, -1, fmt.Errorf("failed to convert minor version part %q: %w", parts[1], err) - } - } - - if len(parts) >= 3 { - micro, err = strconv.Atoi(parts[2]) - if err != nil { - return -1, -1, -1, fmt.Errorf("failed to convert micro version part %q: %w", parts[2], err) - } - } - - return major, minor, micro, nil -} - -// GreaterThanOrEqualTo takes two string versions, parses them into major/minor/micro -// numbers, and compares them to determine whether the first version is greater -// than or equal to the second -func GreaterThanOrEqualTo(version, otherVersion string) (bool, error) { - firstMajor, firstMinor, firstMicro, err := ParseVersion(version) - if err != nil { - return false, err - } - - secondMajor, secondMinor, secondMicro, err := ParseVersion(otherVersion) - if err != nil { - return false, err - } - - if firstMajor > secondMajor { - return true, nil - } else if firstMajor == secondMajor { - if firstMinor > secondMinor { - return true, nil - } else if firstMinor == secondMinor && firstMicro >= secondMicro { - return true, nil - } - } - return false, nil -} - -// GreaterThan returns true if the first version is greater than the second -func GreaterThan(version, otherVersion string) (bool, error) { - firstMajor, firstMinor, firstMicro, err := ParseVersion(version) - if err != nil { - return false, err - } - - secondMajor, secondMinor, secondMicro, err := ParseVersion(otherVersion) - if err != nil { - return false, err - } - - if firstMajor > secondMajor { - return true, nil - } else if firstMajor == secondMajor { - if firstMinor > secondMinor { - return true, nil - } else if firstMinor == secondMinor && firstMicro > secondMicro { - return true, nil - } - } - return false, nil -} diff --git a/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go b/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go deleted file mode 100644 index 25c3810b2a..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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 -// -// http://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. - -package version - -import "fmt" - -type ErrorIncompatible struct { - Config string - Supported []string -} - -func (e *ErrorIncompatible) Details() string { - return fmt.Sprintf("config is %q, plugin supports %q", e.Config, e.Supported) -} - -func (e *ErrorIncompatible) Error() string { - return fmt.Sprintf("incompatible CNI versions: %s", e.Details()) -} - -type Reconciler struct{} - -func (r *Reconciler) Check(configVersion string, pluginInfo PluginInfo) *ErrorIncompatible { - return r.CheckRaw(configVersion, pluginInfo.SupportedVersions()) -} - -func (*Reconciler) CheckRaw(configVersion string, supportedVersions []string) *ErrorIncompatible { - for _, supportedVersion := range supportedVersions { - if configVersion == supportedVersion { - return nil - } - } - - return &ErrorIncompatible{ - Config: configVersion, - Supported: supportedVersions, - } -} diff --git a/vendor/github.com/containernetworking/cni/pkg/version/version.go b/vendor/github.com/containernetworking/cni/pkg/version/version.go deleted file mode 100644 index cfb6a12fa3..0000000000 --- a/vendor/github.com/containernetworking/cni/pkg/version/version.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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 -// -// http://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. - -package version - -import ( - "encoding/json" - "fmt" - - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/create" -) - -// Current reports the version of the CNI spec implemented by this library -func Current() string { - return "1.1.0" -} - -// Legacy PluginInfo describes a plugin that is backwards compatible with the -// CNI spec version 0.1.0. In particular, a runtime compiled against the 0.1.0 -// library ought to work correctly with a plugin that reports support for -// Legacy versions. -// -// Any future CNI spec versions which meet this definition should be added to -// this list. -var ( - Legacy = PluginSupports("0.1.0", "0.2.0") - All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0", "1.0.0", "1.1.0") -) - -// VersionsFrom returns a list of versions starting from min, inclusive -func VersionsStartingFrom(min string) PluginInfo { - out := []string{} - // cheat, just assume ordered - ok := false - for _, v := range All.SupportedVersions() { - if !ok && v == min { - ok = true - } - if ok { - out = append(out, v) - } - } - return PluginSupports(out...) -} - -// Finds a Result object matching the requested version (if any) and asks -// that object to parse the plugin result, returning an error if parsing failed. -func NewResult(version string, resultBytes []byte) (types.Result, error) { - return create.Create(version, resultBytes) -} - -// ParsePrevResult parses a prevResult in a NetConf structure and sets -// the NetConf's PrevResult member to the parsed Result object. -func ParsePrevResult(conf *types.PluginConf) error { - if conf.RawPrevResult == nil { - return nil - } - - // Prior to 1.0.0, Result types may not marshal a CNIVersion. Since the - // result version must match the config version, if the Result's version - // is empty, inject the config version. - if ver, ok := conf.RawPrevResult["CNIVersion"]; !ok || ver == "" { - conf.RawPrevResult["CNIVersion"] = conf.CNIVersion - } - - resultBytes, err := json.Marshal(conf.RawPrevResult) - if err != nil { - return fmt.Errorf("could not serialize prevResult: %w", err) - } - - conf.RawPrevResult = nil - conf.PrevResult, err = create.Create(conf.CNIVersion, resultBytes) - if err != nil { - return fmt.Errorf("could not parse prevResult: %w", err) - } - - return nil -} diff --git a/vendor/github.com/containernetworking/plugins/LICENSE b/vendor/github.com/containernetworking/plugins/LICENSE deleted file mode 100644 index 8dada3edaf..0000000000 --- a/vendor/github.com/containernetworking/plugins/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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 - - http://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. diff --git a/vendor/github.com/containernetworking/plugins/pkg/ns/README.md b/vendor/github.com/containernetworking/plugins/pkg/ns/README.md deleted file mode 100644 index e5fef2db70..0000000000 --- a/vendor/github.com/containernetworking/plugins/pkg/ns/README.md +++ /dev/null @@ -1,41 +0,0 @@ -### Namespaces, Threads, and Go -On Linux each OS thread can have a different network namespace. Go's thread scheduling model switches goroutines between OS threads based on OS thread load and whether the goroutine would block other goroutines. This can result in a goroutine switching network namespaces without notice and lead to errors in your code. - -### Namespace Switching -Switching namespaces with the `ns.Set()` method is not recommended without additional strategies to prevent unexpected namespace changes when your goroutines switch OS threads. - -Go provides the `runtime.LockOSThread()` function to ensure a specific goroutine executes on its current OS thread and prevents any other goroutine from running in that thread until the locked one exits. Careful usage of `LockOSThread()` and goroutines can provide good control over which network namespace a given goroutine executes in. - -For example, you cannot rely on the `ns.Set()` namespace being the current namespace after the `Set()` call unless you do two things. First, the goroutine calling `Set()` must have previously called `LockOSThread()`. Second, you must ensure `runtime.UnlockOSThread()` is not called somewhere in-between. You also cannot rely on the initial network namespace remaining the current network namespace if any other code in your program switches namespaces, unless you have already called `LockOSThread()` in that goroutine. Note that `LockOSThread()` prevents the Go scheduler from optimally scheduling goroutines for best performance, so `LockOSThread()` should only be used in small, isolated goroutines that release the lock quickly. - -### Do() The Recommended Thing -The `ns.Do()` method provides **partial** control over network namespaces for you by implementing these strategies. All code dependent on a particular network namespace (including the root namespace) should be wrapped in the `ns.Do()` method to ensure the correct namespace is selected for the duration of your code. For example: - -```go -err = targetNs.Do(func(hostNs ns.NetNS) error { - linkAttrs := netlink.NewLinkAttrs() - linkAttrs.Name = "dummy0" - dummy := &netlink.Dummy{ - LinkAttrs: linkAttrs, - } - return netlink.LinkAdd(dummy) -}) -``` - -Note this requirement to wrap every network call is very onerous - any libraries you call might call out to network services such as DNS, and all such calls need to be protected after you call `ns.Do()`. All goroutines spawned from within the `ns.Do` will not inherit the new namespace. The CNI plugins all exit very soon after calling `ns.Do()` which helps to minimize the problem. - -When a new thread is spawned in Linux, it inherits the namespace of its parent. In versions of go **prior to 1.10**, if the runtime spawns a new OS thread, it picks the parent randomly. If the chosen parent thread has been moved to a new namespace (even temporarily), the new OS thread will be permanently "stuck in the wrong namespace", and goroutines will non-deterministically switch namespaces as they are rescheduled. - -In short, **there was no safe way to change network namespaces, even temporarily, from within a long-lived, multithreaded Go process**. If you wish to do this, you must use go 1.10 or greater. - - -### Creating network namespaces -Earlier versions of this library managed namespace creation, but as CNI does not actually utilize this feature (and it was essentially unmaintained), it was removed. If you're writing a container runtime, you should implement namespace management yourself. However, there are some gotchas when doing so, especially around handling `/var/run/netns`. A reasonably correct reference implementation, borrowed from `rkt`, can be found in `pkg/testutils/netns_linux.go` if you're in need of a source of inspiration. - - -### Further Reading - - https://github.com/golang/go/wiki/LockOSThread - - http://morsmachine.dk/go-scheduler - - https://github.com/containernetworking/cni/issues/262 - - https://golang.org/pkg/runtime/ - - https://www.weave.works/blog/linux-namespaces-and-go-don-t-mix diff --git a/vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go deleted file mode 100644 index 5a6aaa3339..0000000000 --- a/vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2015-2017 CNI authors -// -// 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 -// -// http://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. - -package ns - -import ( - "fmt" - "os" - "runtime" - "sync" - "syscall" - - "golang.org/x/sys/unix" -) - -// Returns an object representing the current OS thread's network namespace -func GetCurrentNS() (NetNS, error) { - // Lock the thread in case other goroutine executes in it and changes its - // network namespace after getCurrentThreadNetNSPath(), otherwise it might - // return an unexpected network namespace. - runtime.LockOSThread() - defer runtime.UnlockOSThread() - return getCurrentNSNoLock() -} - -func getCurrentNSNoLock() (NetNS, error) { - return GetNS(getCurrentThreadNetNSPath()) -} - -func getCurrentThreadNetNSPath() string { - // /proc/self/ns/net returns the namespace of the main thread, not - // of whatever thread this goroutine is running on. Make sure we - // use the thread's net namespace since the thread is switching around - return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid()) -} - -func (ns *netNS) Close() error { - if err := ns.errorIfClosed(); err != nil { - return err - } - - if err := ns.file.Close(); err != nil { - return fmt.Errorf("Failed to close %q: %v", ns.file.Name(), err) - } - ns.closed = true - - return nil -} - -func (ns *netNS) Set() error { - if err := ns.errorIfClosed(); err != nil { - return err - } - - if err := unix.Setns(int(ns.Fd()), unix.CLONE_NEWNET); err != nil { - return fmt.Errorf("Error switching to ns %v: %v", ns.file.Name(), err) - } - - return nil -} - -type NetNS interface { - // Executes the passed closure in this object's network namespace, - // attempting to restore the original namespace before returning. - // However, since each OS thread can have a different network namespace, - // and Go's thread scheduling is highly variable, callers cannot - // guarantee any specific namespace is set unless operations that - // require that namespace are wrapped with Do(). Also, no code called - // from Do() should call runtime.UnlockOSThread(), or the risk - // of executing code in an incorrect namespace will be greater. See - // https://github.com/golang/go/wiki/LockOSThread for further details. - Do(toRun func(NetNS) error) error - - // Sets the current network namespace to this object's network namespace. - // Note that since Go's thread scheduling is highly variable, callers - // cannot guarantee the requested namespace will be the current namespace - // after this function is called; to ensure this wrap operations that - // require the namespace with Do() instead. - Set() error - - // Returns the filesystem path representing this object's network namespace - Path() string - - // Returns a file descriptor representing this object's network namespace - Fd() uintptr - - // Cleans up this instance of the network namespace; if this instance - // is the last user the namespace will be destroyed - Close() error -} - -type netNS struct { - file *os.File - closed bool -} - -// netNS implements the NetNS interface -var _ NetNS = &netNS{} - -const ( - // https://github.com/torvalds/linux/blob/master/include/uapi/linux/magic.h - NSFS_MAGIC = unix.NSFS_MAGIC - PROCFS_MAGIC = unix.PROC_SUPER_MAGIC -) - -type NSPathNotExistErr struct{ msg string } - -func (e NSPathNotExistErr) Error() string { return e.msg } - -type NSPathNotNSErr struct{ msg string } - -func (e NSPathNotNSErr) Error() string { return e.msg } - -func IsNSorErr(nspath string) error { - stat := syscall.Statfs_t{} - if err := syscall.Statfs(nspath, &stat); err != nil { - if os.IsNotExist(err) { - err = NSPathNotExistErr{msg: fmt.Sprintf("failed to Statfs %q: %v", nspath, err)} - } else { - err = fmt.Errorf("failed to Statfs %q: %v", nspath, err) - } - return err - } - - switch stat.Type { - case PROCFS_MAGIC, NSFS_MAGIC: - return nil - default: - return NSPathNotNSErr{msg: fmt.Sprintf("unknown FS magic on %q: %x", nspath, stat.Type)} - } -} - -// Returns an object representing the namespace referred to by @path -func GetNS(nspath string) (NetNS, error) { - err := IsNSorErr(nspath) - if err != nil { - return nil, err - } - - fd, err := os.Open(nspath) - if err != nil { - return nil, err - } - - return &netNS{file: fd}, nil -} - -// Returns a new empty NetNS. -// Calling Close() let the kernel garbage collect the network namespace. -func TempNetNS() (NetNS, error) { - var tempNS NetNS - var err error - var wg sync.WaitGroup - wg.Add(1) - - // Create the new namespace in a new goroutine so that if we later fail - // to switch the namespace back to the original one, we can safely - // leave the thread locked to die without a risk of the current thread - // left lingering with incorrect namespace. - go func() { - defer wg.Done() - runtime.LockOSThread() - - var threadNS NetNS - // save a handle to current network namespace - threadNS, err = getCurrentNSNoLock() - if err != nil { - err = fmt.Errorf("failed to open current namespace: %v", err) - return - } - defer threadNS.Close() - - // create the temporary network namespace - err = unix.Unshare(unix.CLONE_NEWNET) - if err != nil { - return - } - - // get a handle to the temporary network namespace - tempNS, err = getCurrentNSNoLock() - - err2 := threadNS.Set() - if err2 == nil { - // Unlock the current thread only when we successfully switched back - // to the original namespace; otherwise leave the thread locked which - // will force the runtime to scrap the current thread, that is maybe - // not as optimal but at least always safe to do. - runtime.UnlockOSThread() - } - }() - - wg.Wait() - return tempNS, err -} - -func (ns *netNS) Path() string { - return ns.file.Name() -} - -func (ns *netNS) Fd() uintptr { - return ns.file.Fd() -} - -func (ns *netNS) errorIfClosed() error { - if ns.closed { - return fmt.Errorf("%q has already been closed", ns.file.Name()) - } - return nil -} - -func (ns *netNS) Do(toRun func(NetNS) error) error { - if err := ns.errorIfClosed(); err != nil { - return err - } - - containedCall := func(hostNS NetNS) error { - threadNS, err := getCurrentNSNoLock() - if err != nil { - return fmt.Errorf("failed to open current netns: %v", err) - } - defer threadNS.Close() - - // switch to target namespace - if err = ns.Set(); err != nil { - return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err) - } - defer func() { - err := threadNS.Set() // switch back - if err == nil { - // Unlock the current thread only when we successfully switched back - // to the original namespace; otherwise leave the thread locked which - // will force the runtime to scrap the current thread, that is maybe - // not as optimal but at least always safe to do. - runtime.UnlockOSThread() - } - }() - - return toRun(hostNS) - } - - // save a handle to current network namespace - hostNS, err := GetCurrentNS() - if err != nil { - return fmt.Errorf("Failed to open current namespace: %v", err) - } - defer hostNS.Close() - - var wg sync.WaitGroup - wg.Add(1) - - // Start the callback in a new green thread so that if we later fail - // to switch the namespace back to the original one, we can safely - // leave the thread locked to die without a risk of the current thread - // left lingering with incorrect namespace. - var innerError error - go func() { - defer wg.Done() - runtime.LockOSThread() - innerError = containedCall(hostNS) - }() - wg.Wait() - - return innerError -} - -// WithNetNSPath executes the passed closure under the given network -// namespace, restoring the original namespace afterwards. -func WithNetNSPath(nspath string, toRun func(NetNS) error) error { - ns, err := GetNS(nspath) - if err != nil { - return err - } - defer ns.Close() - return ns.Do(toRun) -} diff --git a/vendor/modules.txt b/vendor/modules.txt index e005fa76c4..8d4f44022f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -62,21 +62,6 @@ github.com/containerd/platforms ## explicit; go 1.24.0 github.com/containerd/stargz-snapshotter/estargz github.com/containerd/stargz-snapshotter/estargz/errorutil -# github.com/containernetworking/cni v1.3.0 -## explicit; go 1.21 -github.com/containernetworking/cni/libcni -github.com/containernetworking/cni/pkg/invoke -github.com/containernetworking/cni/pkg/types -github.com/containernetworking/cni/pkg/types/020 -github.com/containernetworking/cni/pkg/types/040 -github.com/containernetworking/cni/pkg/types/100 -github.com/containernetworking/cni/pkg/types/create -github.com/containernetworking/cni/pkg/types/internal -github.com/containernetworking/cni/pkg/utils -github.com/containernetworking/cni/pkg/version -# github.com/containernetworking/plugins v1.9.0 -## explicit; go 1.24.2 -github.com/containernetworking/plugins/pkg/ns # github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 ## explicit github.com/containers/libtrust