From e3274af70a2423a2cec89d8483b2785555ebfc51 Mon Sep 17 00:00:00 2001 From: Lokesh Mandvekar Date: Tue, 3 Mar 2026 18:35:00 +0530 Subject: [PATCH 1/9] Makefile: remove CNI build infrastructure Drop the build-amd64-cni target and cni-tagged unit tests from the Makefile as part of removing CNI support in favor of netavark-only networking for Podman 6. Signed-off-by: Lokesh Mandvekar --- common/.golangci.yml | 1 - common/Makefile | 9 ++------- 2 files changed, 2 insertions(+), 8 deletions(-) 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 From d637f1e299154532971063573a7ff2a9aa00880f Mon Sep 17 00:00:00 2001 From: Lokesh Mandvekar Date: Tue, 3 Mar 2026 18:36:37 +0530 Subject: [PATCH 2/9] libnetwork: remove CNI network backend selection logic Remove the CNI-tagged interface files and simplify NetworkBackend() to only support netavark. Any other backend value results in an error. Signed-off-by: Lokesh Mandvekar --- common/libnetwork/network/interface.go | 84 ++----------- common/libnetwork/network/interface_cni.go | 119 ------------------ .../network/interface_cni_unsupported.go | 30 ----- .../libnetwork/network/interface_freebsd.go | 2 - common/libnetwork/network/interface_linux.go | 2 - 5 files changed, 10 insertions(+), 227 deletions(-) delete mode 100644 common/libnetwork/network/interface_cni.go delete mode 100644 common/libnetwork/network/interface_cni_unsupported.go 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. From 3b52be5994052f6b387df9da3c9ccd955fd316fc Mon Sep 17 00:00:00 2001 From: Lokesh Mandvekar Date: Tue, 3 Mar 2026 18:39:04 +0530 Subject: [PATCH 3/9] libnetwork: remove CNI-specific code paths from rootless networking Remove the NetworkBackend type and CNI constants from rootlessnetns, drop the CNI /var/lib/cni mount logic, and update comments throughout. Signed-off-by: Lokesh Mandvekar --- .../internal/rootlessnetns/netns.go | 8 --- .../internal/rootlessnetns/netns_freebsd.go | 2 +- .../internal/rootlessnetns/netns_linux.go | 60 +++---------------- common/libnetwork/netavark/config.go | 2 +- common/libnetwork/netavark/network.go | 4 +- 5 files changed, 12 insertions(+), 64 deletions(-) delete mode 100644 common/libnetwork/internal/rootlessnetns/netns.go 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..0973b12609 100644 --- a/common/libnetwork/internal/rootlessnetns/netns_linux.go +++ b/common/libnetwork/internal/rootlessnetns/netns_linux.go @@ -41,9 +41,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 +49,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 +83,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 } @@ -349,16 +343,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 +378,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 +494,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 +516,6 @@ func (n *Netns) setupMounts() error { return nil } -func (n *Netns) mountCNIVarDir() error { - varDir := "" - varTarget := persistentCNIDir - // we can only mount to a target dir which exists, check /var/lib/cni recursively - // while we could always use /var there are cases where a user might store the cni - // configs under /var/custom and this would break - for { - if err := fileutils.Exists(varTarget); err == nil { - varDir = n.getPath(varTarget) - break - } - varTarget = filepath.Dir(varTarget) - if varTarget == "/" { - break - } - } - if varDir == "" { - return errors.New("failed to stat /var directory") - } - if err := os.MkdirAll(varDir, 0o700); err != nil { - return wrapError("create var dir", err) - } - // make sure to mount var first - err := unix.Mount(varDir, varTarget, none, unix.MS_BIND, "") - if err != nil { - return wrapError(fmt.Sprintf("mount %q to %q", varDir, varTarget), err) - } - return nil -} - func (n *Netns) runInner(toRun func() error, cleanup bool) (err error) { nsRef, newNs, err := n.getOrCreateNetns() if err != nil { diff --git a/common/libnetwork/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/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 } From 6c9df284f49a74464fdacb17b96f63388c078f93 Mon Sep 17 00:00:00 2001 From: Lokesh Mandvekar Date: Tue, 3 Mar 2026 18:47:22 +0530 Subject: [PATCH 4/9] config: remove CNI configuration and types Remove the CNI network backend constant, CNIPluginDirs config field, DefaultCNIPluginDirs variable, and cni_plugin_dirs from config files and tests. Signed-off-by: Lokesh Mandvekar --- common/libnetwork/types/const.go | 5 +- common/pkg/config/config.go | 5 +- common/pkg/config/config_local_test.go | 21 -------- common/pkg/config/config_remote_test.go | 54 ------------------- common/pkg/config/config_test.go | 1 - common/pkg/config/containers.conf | 23 ++------ common/pkg/config/containers.conf-freebsd | 23 ++------ common/pkg/config/default.go | 9 ---- .../config/testdata/containers_comment.conf | 12 ++--- .../config/testdata/containers_default.conf | 14 ++--- .../config/testdata/containers_invalid.conf | 11 ++-- 11 files changed, 21 insertions(+), 157 deletions(-) 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] From 9abee9a2f00b011c024c690b513dff543d926ca3 Mon Sep 17 00:00:00 2001 From: Lokesh Mandvekar Date: Tue, 3 Mar 2026 18:51:28 +0530 Subject: [PATCH 5/9] libnetwork: remove CNI package Delete the entire libnetwork/cni/ directory including all source files, tests, and test fixtures. Signed-off-by: Lokesh Mandvekar --- common/libnetwork/cni/README.md | 10 - common/libnetwork/cni/cni_conversion.go | 461 ----- common/libnetwork/cni/cni_exec.go | 122 -- common/libnetwork/cni/cni_suite_test.go | 49 - common/libnetwork/cni/cni_types.go | 286 --- common/libnetwork/cni/config.go | 246 --- common/libnetwork/cni/config_freebsd.go | 16 - common/libnetwork/cni/config_linux.go | 19 - common/libnetwork/cni/config_test.go | 1749 ----------------- common/libnetwork/cni/network.go | 359 ---- common/libnetwork/cni/run.go | 302 --- common/libnetwork/cni/run_freebsd.go | 19 - common/libnetwork/cni/run_linux.go | 17 - common/libnetwork/cni/run_test.go | 1407 ------------- .../cni/testfiles/invalid/broken.conflist | 25 - .../invalid/invalid_gateway.conflist | 44 - .../testfiles/invalid/invalidname.conflist | 42 - .../cni/testfiles/invalid/noname.conflist | 41 - .../cni/testfiles/invalid/noplugin.conflist | 5 - .../cni/testfiles/invalid/samename1.conflist | 42 - .../cni/testfiles/invalid/samename2.conflist | 42 - .../cni/testfiles/valid/87-podman.conflist | 37 - .../cni/testfiles/valid/bridge.conflist | 44 - .../cni/testfiles/valid/dualstack.conflist | 51 - .../cni/testfiles/valid/internal.conflist | 40 - .../cni/testfiles/valid/ipam-empty.conflist | 27 - .../cni/testfiles/valid/ipam-none.conflist | 26 - .../cni/testfiles/valid/ipam-static.conflist | 42 - .../cni/testfiles/valid/isolate.conflist | 46 - .../cni/testfiles/valid/label.conflist | 47 - .../cni/testfiles/valid/macvlan.conflist | 13 - .../cni/testfiles/valid/macvlan_mtu.conflist | 14 - .../cni/testfiles/valid/mtu.conflist | 42 - .../cni/testfiles/valid/vlan.conflist | 43 - 34 files changed, 5775 deletions(-) delete mode 100644 common/libnetwork/cni/README.md delete mode 100644 common/libnetwork/cni/cni_conversion.go delete mode 100644 common/libnetwork/cni/cni_exec.go delete mode 100644 common/libnetwork/cni/cni_suite_test.go delete mode 100644 common/libnetwork/cni/cni_types.go delete mode 100644 common/libnetwork/cni/config.go delete mode 100644 common/libnetwork/cni/config_freebsd.go delete mode 100644 common/libnetwork/cni/config_linux.go delete mode 100644 common/libnetwork/cni/config_test.go delete mode 100644 common/libnetwork/cni/network.go delete mode 100644 common/libnetwork/cni/run.go delete mode 100644 common/libnetwork/cni/run_freebsd.go delete mode 100644 common/libnetwork/cni/run_linux.go delete mode 100644 common/libnetwork/cni/run_test.go delete mode 100644 common/libnetwork/cni/testfiles/invalid/broken.conflist delete mode 100644 common/libnetwork/cni/testfiles/invalid/invalid_gateway.conflist delete mode 100644 common/libnetwork/cni/testfiles/invalid/invalidname.conflist delete mode 100644 common/libnetwork/cni/testfiles/invalid/noname.conflist delete mode 100644 common/libnetwork/cni/testfiles/invalid/noplugin.conflist delete mode 100644 common/libnetwork/cni/testfiles/invalid/samename1.conflist delete mode 100644 common/libnetwork/cni/testfiles/invalid/samename2.conflist delete mode 100644 common/libnetwork/cni/testfiles/valid/87-podman.conflist delete mode 100644 common/libnetwork/cni/testfiles/valid/bridge.conflist delete mode 100644 common/libnetwork/cni/testfiles/valid/dualstack.conflist delete mode 100644 common/libnetwork/cni/testfiles/valid/internal.conflist delete mode 100644 common/libnetwork/cni/testfiles/valid/ipam-empty.conflist delete mode 100644 common/libnetwork/cni/testfiles/valid/ipam-none.conflist delete mode 100644 common/libnetwork/cni/testfiles/valid/ipam-static.conflist delete mode 100644 common/libnetwork/cni/testfiles/valid/isolate.conflist delete mode 100644 common/libnetwork/cni/testfiles/valid/label.conflist delete mode 100644 common/libnetwork/cni/testfiles/valid/macvlan.conflist delete mode 100644 common/libnetwork/cni/testfiles/valid/macvlan_mtu.conflist delete mode 100644 common/libnetwork/cni/testfiles/valid/mtu.conflist delete mode 100644 common/libnetwork/cni/testfiles/valid/vlan.conflist 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" - } - ] -} From 4dd4c7a7758c712e1e92d1782ba6f06301f9ab00 Mon Sep 17 00:00:00 2001 From: Lokesh Mandvekar Date: Fri, 13 Mar 2026 16:58:57 +0530 Subject: [PATCH 6/9] netns: inline containernetworking/plugins/pkg/ns Copy the NetNS interface, netNS struct, GetNS(), WithNetNSPath(), GetCurrentNS(), and related types from the vendored containernetworking/plugins/pkg/ns package into common/pkg/netns. Update all consumers to import from common/pkg/netns instead. This removes the runtime dependency on containernetworking/plugins for network namespace management. Signed-off-by: Lokesh Mandvekar --- .../internal/rootlessnetns/netns_linux.go | 9 +- common/libnetwork/netavark/run_test.go | 29 ++- common/libnetwork/pasta/pasta_linux.go | 4 +- common/libnetwork/slirp4netns/slirp4netns.go | 4 +- common/pkg/netns/netns_linux.go | 220 +++++++++++++++++- 5 files changed, 236 insertions(+), 30 deletions(-) diff --git a/common/libnetwork/internal/rootlessnetns/netns_linux.go b/common/libnetwork/internal/rootlessnetns/netns_linux.go index 0973b12609..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" @@ -101,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) @@ -134,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) } @@ -533,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/run_test.go b/common/libnetwork/netavark/run_test.go index b6d7f32747..ed1de4eb83 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. 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") @@ -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/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/pkg/netns/netns_linux.go b/common/pkg/netns/netns_linux.go index 4283ffd8d7..10116553dc 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" @@ -56,13 +56,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 +210,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 +255,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. @@ -293,3 +295,209 @@ func UnmountNS(nsPath string) error { return fmt.Errorf("failed to remove ns path (timeout after 60s): %w", err) } + +// The following types and functions were inlined from +// github.com/containernetworking/plugins/pkg/ns. + +// 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 +} From 917f9dc9213ee2318610c4a55a0b81a7a9ccecef Mon Sep 17 00:00:00 2001 From: Lokesh Mandvekar Date: Fri, 13 Mar 2026 17:00:45 +0530 Subject: [PATCH 7/9] netns: reorder inlined code for logical grouping Move the NetNS interface, netNS struct, and related types and methods above the existing netns functions so that type definitions precede their usage. No functional changes. Signed-off-by: Lokesh Mandvekar --- common/pkg/netns/netns_linux.go | 409 ++++++++++++++++---------------- 1 file changed, 203 insertions(+), 206 deletions(-) diff --git a/common/pkg/netns/netns_linux.go b/common/pkg/netns/netns_linux.go index 10116553dc..c2ee879d62 100644 --- a/common/pkg/netns/netns_linux.go +++ b/common/pkg/netns/netns_linux.go @@ -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) { @@ -295,209 +498,3 @@ func UnmountNS(nsPath string) error { return fmt.Errorf("failed to remove ns path (timeout after 60s): %w", err) } - -// The following types and functions were inlined from -// github.com/containernetworking/plugins/pkg/ns. - -// 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 -} From 893bb58070b85a4b0966a2ef1d7ee06e93d07955 Mon Sep 17 00:00:00 2001 From: Lokesh Mandvekar Date: Tue, 3 Mar 2026 19:01:43 +0530 Subject: [PATCH 8/9] go.mod: remove containernetworking dependencies Remove github.com/containernetworking/cni and github.com/containernetworking/plugins from go.mod and vendor. Signed-off-by: Lokesh Mandvekar --- common/go.mod | 2 - common/go.sum | 4 - .../containernetworking/cni/LICENSE | 202 ---- .../containernetworking/cni/libcni/api.go | 900 ------------------ .../containernetworking/cni/libcni/conf.go | 445 --------- .../cni/pkg/invoke/args.go | 128 --- .../cni/pkg/invoke/delegate.go | 89 -- .../cni/pkg/invoke/exec.go | 187 ---- .../cni/pkg/invoke/find.go | 48 - .../cni/pkg/invoke/os_unix.go | 21 - .../cni/pkg/invoke/os_windows.go | 18 - .../cni/pkg/invoke/raw_exec.go | 88 -- .../cni/pkg/types/020/types.go | 189 ---- .../cni/pkg/types/040/types.go | 306 ------ .../cni/pkg/types/100/types.go | 352 ------- .../containernetworking/cni/pkg/types/args.go | 122 --- .../cni/pkg/types/create/create.go | 59 -- .../cni/pkg/types/internal/convert.go | 92 -- .../cni/pkg/types/internal/create.go | 66 -- .../cni/pkg/types/types.go | 325 ------- .../cni/pkg/utils/utils.go | 82 -- .../cni/pkg/version/conf.go | 26 - .../cni/pkg/version/plugin.go | 168 ---- .../cni/pkg/version/reconcile.go | 49 - .../cni/pkg/version/version.go | 90 -- .../containernetworking/plugins/LICENSE | 201 ---- .../plugins/pkg/ns/README.md | 41 - .../plugins/pkg/ns/ns_linux.go | 286 ------ vendor/modules.txt | 15 - 29 files changed, 4601 deletions(-) delete mode 100644 vendor/github.com/containernetworking/cni/LICENSE delete mode 100644 vendor/github.com/containernetworking/cni/libcni/api.go delete mode 100644 vendor/github.com/containernetworking/cni/libcni/conf.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/invoke/args.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/invoke/exec.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/invoke/find.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/invoke/os_unix.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/invoke/os_windows.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/types/020/types.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/types/040/types.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/types/100/types.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/types/args.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/types/create/create.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/types/internal/create.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/types/types.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/utils/utils.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/version/conf.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/version/plugin.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/version/reconcile.go delete mode 100644 vendor/github.com/containernetworking/cni/pkg/version/version.go delete mode 100644 vendor/github.com/containernetworking/plugins/LICENSE delete mode 100644 vendor/github.com/containernetworking/plugins/pkg/ns/README.md delete mode 100644 vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go 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/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 From 40c034c1a9963844f1e222429a1da40b74a129a2 Mon Sep 17 00:00:00 2001 From: Lokesh Mandvekar Date: Tue, 3 Mar 2026 19:04:17 +0530 Subject: [PATCH 9/9] docs: update documentation to remove CNI references Update containers.conf.5.md to reflect netavark-only networking and remove cni_plugin_dirs documentation. Clean up remaining CNI references in netavark test file comments. Signed-off-by: Lokesh Mandvekar --- common/docs/containers.conf.5.md | 32 ++++------------------- common/libnetwork/netavark/config_test.go | 2 +- common/libnetwork/netavark/ipam_test.go | 2 +- common/libnetwork/netavark/run_test.go | 6 ++--- 4 files changed, 10 insertions(+), 32 deletions(-) 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/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/run_test.go b/common/libnetwork/netavark/run_test.go index ed1de4eb83..416c4ba7d0 100644 --- a/common/libnetwork/netavark/run_test.go +++ b/common/libnetwork/netavark/run_test.go @@ -41,7 +41,7 @@ var _ = Describe("run netavark", func() { ) // 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(_ netns.NetNS) error { defer GinkgoRecover() @@ -62,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") } @@ -107,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") } })