diff options
author | Paul Holzinger <pholzing@redhat.com> | 2021-12-17 14:46:15 +0100 |
---|---|---|
committer | Paul Holzinger <pholzing@redhat.com> | 2022-01-12 17:07:30 +0100 |
commit | 495884b3195de482dc610a2a002db7e053188a32 (patch) | |
tree | 2a6f23db066cd52aa366991b0b34d7b919368ddc /libpod/network | |
parent | 2cdab5d53923784e72020d70ee9375518f19f9b6 (diff) | |
download | podman-495884b3195de482dc610a2a002db7e053188a32.tar.gz podman-495884b3195de482dc610a2a002db7e053188a32.tar.bz2 podman-495884b3195de482dc610a2a002db7e053188a32.zip |
use libnetwork from c/common
The libpod/network packages were moved to c/common so that buildah can
use it as well. To prevent duplication use it in podman as well and
remove it from here.
Signed-off-by: Paul Holzinger <pholzing@redhat.com>
Diffstat (limited to 'libpod/network')
64 files changed, 0 insertions, 9911 deletions
diff --git a/libpod/network/cni/README.md b/libpod/network/cni/README.md deleted file mode 100644 index 6f57feff5..000000000 --- a/libpod/network/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/libpod/network/cni/cni_conversion.go b/libpod/network/cni/cni_conversion.go deleted file mode 100644 index 788165b5e..000000000 --- a/libpod/network/cni/cni_conversion.go +++ /dev/null @@ -1,378 +0,0 @@ -// +build linux - -package cni - -import ( - "encoding/json" - "io/ioutil" - "net" - "os" - "path/filepath" - "strconv" - "strings" - "syscall" - "time" - - "github.com/containernetworking/cni/libcni" - internalutil "github.com/containers/podman/v3/libpod/network/internal/util" - "github.com/containers/podman/v3/libpod/network/types" - "github.com/containers/podman/v3/libpod/network/util" - pkgutil "github.com/containers/podman/v3/pkg/util" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -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]interface{}) - err := json.Unmarshal(conf.Bytes, &cniJSON) - if err != nil { - return nil, errors.Wrapf(err, "failed to unmarshal network config %s", conf.Name) - } - if args, ok := cniJSON["args"]; ok { - if key, ok := args.(map[string]interface{}); ok { - // read network labels and options from the conf file - network.Labels = getNetworkArgsFromConfList(key, podmanLabelKey) - network.Options = getNetworkArgsFromConfList(key, podmanOptionsKey) - } - } - - f, err := os.Stat(confPath) - if err != nil { - return nil, err - } - stat := f.Sys().(*syscall.Stat_t) - network.Created = time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)) - - 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, errors.Wrapf(err, "failed to unmarshal the bridge plugin config in %s", confPath) - } - 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["mtu"] = strconv.Itoa(bridge.MTU) - } - if bridge.Vlan != 0 { - network.Options["vlan"] = 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, errors.Wrapf(err, "failed to unmarshal the macvlan plugin config in %s", confPath) - } - network.NetworkInterface = vlan.Master - - // set network options - if vlan.MTU != 0 { - network.Options["mtu"] = strconv.Itoa(vlan.MTU) - } - - if vlan.Mode != "" { - network.Options["mode"] = 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") - - return &network, nil -} - -func findPluginByName(plugins []*libcni.NetworkConfig, name string) bool { - for _, plugin := range plugins { - if plugin.Network.Type == name { - return true - } - } - return false -} - -// 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 { - if ipam.PluginType == types.DHCPIPAMDriver { - network.IPAMOptions["driver"] = types.DHCPIPAMDriver - return nil - } - - if ipam.PluginType != types.HostLocalIPAMDriver { - return errors.Errorf("unsupported ipam plugin %s in %s", ipam.PluginType, confPath) - } - - network.IPAMOptions["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 errors.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 errors.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 errors.Errorf("failed to parse range start ip %s", ipam.RangeStart) - } - } - if ipam.RangeEnd != "" { - rangeEnd = net.ParseIP(ipam.RangeEnd) - if rangeEnd == nil { - return errors.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) - } - } - return nil -} - -// getNetworkArgsFromConfList returns the map of args in a conflist, argType should be labels or options -func getNetworkArgsFromConfList(args map[string]interface{}, argType string) map[string]string { - if args, ok := args[argType]; ok { - if labels, ok := args.(map[string]interface{}); 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 - ) - if len(network.Subnets) > 0 { - for _, subnet := range network.Subnets { - route, err := newIPAMDefaultRoute(util.IsIPv6(subnet.Subnet.IP)) - if err != nil { - return nil, "", err - } - routes = append(routes, route) - ipam := newIPAMLocalHostRange(subnet.Subnet, subnet.LeaseRange, subnet.Gateway) - ipamRanges = append(ipamRanges, []ipamLocalHostRangeConf{*ipam}) - } - ipamConf = newIPAMHostLocalConf(routes, ipamRanges) - } else { - ipamConf = ipamConfig{PluginType: "dhcp"} - } - - vlan := 0 - mtu := 0 - vlanPluginMode := "" - for k, v := range network.Options { - switch k { - case "mtu": - mtu, err = internalutil.ParseMTU(v) - if err != nil { - return nil, "", err - } - - case "vlan": - vlan, err = internalutil.ParseVlan(v) - if err != nil { - return nil, "", err - } - - case "mode": - switch network.Driver { - case types.MacVLANNetworkDriver: - if !pkgutil.StringInSlice(v, []string{"", "bridge", "private", "vepa", "passthru"}) { - return nil, "", errors.Errorf("unknown macvlan mode %q", v) - } - case types.IPVLANNetworkDriver: - if !pkgutil.StringInSlice(v, []string{"", "l2", "l3", "l3s"}) { - return nil, "", errors.Errorf("unknown ipvlan mode %q", v) - } - default: - return nil, "", errors.Errorf("cannot set option \"mode\" with driver %q", network.Driver) - } - vlanPluginMode = v - - default: - return nil, "", errors.Errorf("unsupported network option %s", k) - } - } - - 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 []interface{} - - switch network.Driver { - case types.BridgeNetworkDriver: - bridge := newHostLocalBridge(network.NetworkInterface, isGateway, ipMasq, mtu, vlan, ipamConf) - plugins = append(plugins, bridge, newPortMapPlugin(), newFirewallPlugin(), 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, vlanPluginMode, mtu, ipamConf)) - - case types.IPVLANNetworkDriver: - plugins = append(plugins, newVLANPlugin(types.IPVLANNetworkDriver, network.NetworkInterface, vlanPluginMode, mtu, ipamConf)) - - default: - return nil, "", errors.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 { - cniPathName = filepath.Join(n.cniConfigDir, network.Name+".conflist") - err = ioutil.WriteFile(cniPathName, b, 0644) - if err != nil { - return nil, "", err - } - f, err := os.Stat(cniPathName) - if err != nil { - return nil, "", err - } - stat := f.Sys().(*syscall.Stat_t) - network.Created = time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)) - } 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") - } - protocols := strings.Split(port.Protocol, ",") - - for _, protocol := range protocols { - if !pkgutil.StringInSlice(protocol, []string{"tcp", "udp", "sctp"}) { - return nil, errors.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 -} diff --git a/libpod/network/cni/cni_exec.go b/libpod/network/cni/cni_exec.go deleted file mode 100644 index ae857bcfb..000000000 --- a/libpod/network/cni/cni_exec.go +++ /dev/null @@ -1,110 +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. - -// +build linux - -package cni - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "os/exec" - "path/filepath" - - "github.com/containernetworking/cni/pkg/invoke" - "github.com/containernetworking/cni/pkg/version" - "github.com/containers/podman/v3/pkg/rootless" -) - -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 !rootless.IsRootless() { - c.Env = append(c.Env, "XDG_RUNTIME_DIR=") - } - - 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 []byte, 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/libpod/network/cni/cni_suite_test.go b/libpod/network/cni/cni_suite_test.go deleted file mode 100644 index f98869c96..000000000 --- a/libpod/network/cni/cni_suite_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// +build linux - -package cni_test - -import ( - "os" - "path/filepath" - "testing" - - "github.com/containers/podman/v3/libpod/network/cni" - "github.com/containers/podman/v3/libpod/network/types" - "github.com/containers/podman/v3/test/utils" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -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, machine bool) (types.ContainerNetwork, error) { - return cni.NewCNINetworkInterface(cni.InitConfig{ - CNIConfigDir: cniConfDir, - CNIPluginDirs: cniPluginDirs, - IsMachine: machine, - LockFile: filepath.Join(cniConfDir, "cni.lock"), - }) -} - -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") -} - -func SkipIfNotFedora(msg string) { - info := utils.GetHostDistributionInfo() - if info.Distribution != "fedora" { - Skip("Test can only run on Fedora: " + msg) - } -} diff --git a/libpod/network/cni/cni_types.go b/libpod/network/cni/cni_types.go deleted file mode 100644 index e5eb777de..000000000 --- a/libpod/network/cni/cni_types.go +++ /dev/null @@ -1,281 +0,0 @@ -// +build linux - -package cni - -import ( - "net" - "os" - "path/filepath" - - "github.com/containers/podman/v3/libpod/network/types" -) - -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" -) - -// 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"` -} - -// 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]interface{} - -// 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 int, vlan int, ipamConf ipamConfig) *hostLocalBridge { - caps := make(map[string]bool) - caps["ips"] = true - bridge := hostLocalBridge{ - PluginType: "bridge", - BrName: name, - IsGW: isGateWay, - IPMasq: ipMasq, - MTU: mtu, - HairpinMode: true, - Vlan: vlan, - 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 = caps - } - 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 { - caps := make(map[string]bool) - caps["portMappings"] = true - p := portMapConfig{ - PluginType: "portmap", - Capabilities: caps, - } - return p -} - -// newFirewallPlugin creates a generic firewall plugin -func newFirewallPlugin() firewallConfig { - return firewallConfig{ - PluginType: "firewall", - } -} - -// newTuningPlugin creates a generic tuning section -func newTuningPlugin() tuningConfig { - return tuningConfig{ - PluginType: "tuning", - } -} - -// newDNSNamePlugin creates the dnsname config with a given -// domainname -func newDNSNamePlugin(domainName string) dnsNameConfig { - caps := make(map[string]bool, 1) - caps["aliases"] = true - return dnsNameConfig{ - PluginType: "dnsname", - DomainName: domainName, - Capabilities: caps, - } -} - -// hasDNSNamePlugin looks to see if the dnsname cni plugin is present -func hasDNSNamePlugin(paths []string) bool { - for _, p := range paths { - if _, err := os.Stat(filepath.Join(p, "dnsname")); err == nil { - return true - } - } - return false -} - -// newVLANPlugin creates a macvlanconfig with a given device name -func newVLANPlugin(pluginType, device, mode string, mtu int, ipam ipamConfig) VLANConfig { - m := VLANConfig{ - PluginType: pluginType, - 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 ipam.PluginType == types.HostLocalIPAMDriver { - m.Capabilities = caps - } - return m -} diff --git a/libpod/network/cni/config.go b/libpod/network/cni/config.go deleted file mode 100644 index 5d587da23..000000000 --- a/libpod/network/cni/config.go +++ /dev/null @@ -1,208 +0,0 @@ -// +build linux - -package cni - -import ( - "net" - "os" - - "github.com/containers/podman/v3/libpod/define" - internalutil "github.com/containers/podman/v3/libpod/network/internal/util" - "github.com/containers/podman/v3/libpod/network/types" - pkgutil "github.com/containers/podman/v3/pkg/util" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" -) - -// 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) (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 { - 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 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, errors.Wrap(define.ErrInvalidArg, "ID can not be set for network create") - } - - err := internalutil.CommonNetworkCreate(n, &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 { - usedNetworks, err = internalutil.GetUsedSubnets(n) - if err != nil { - return nil, err - } - } - - switch newNetwork.Driver { - case types.BridgeNetworkDriver: - err = internalutil.CreateBridge(n, &newNetwork, usedNetworks) - if err != nil { - return nil, err - } - case types.MacVLANNetworkDriver, types.IPVLANNetworkDriver: - err = createIPMACVLAN(&newNetwork) - if err != nil { - return nil, err - } - default: - return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported driver %s", newNetwork.Driver) - } - - err = internalutil.ValidateSubnets(&newNetwork, usedNetworks) - if err != nil { - return nil, err - } - - // generate the network ID - newNetwork.ID = getNetworkIDFromName(newNetwork.Name) - - // 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 errors.Errorf("default network %s cannot be removed", n.defaultNetwork) - } - - // Remove the bridge network interface on the host. - if network.libpodNet.Driver == types.BridgeNetworkDriver { - link, err := netlink.LinkByName(network.libpodNet.NetworkInterface) - 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", network.libpodNet.NetworkInterface, err) - } - } - } - - 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.Internal { - return errors.New("internal is not supported with macvlan") - } - if network.NetworkInterface != "" { - interfaceNames, err := internalutil.GetLiveNetworkNames() - if err != nil { - return err - } - if !pkgutil.StringInSlice(network.NetworkInterface, interfaceNames) { - return errors.Errorf("parent interface %s does not exists", network.NetworkInterface) - } - } - if len(network.Subnets) == 0 { - network.IPAMOptions["driver"] = types.DHCPIPAMDriver - } else { - network.IPAMOptions["driver"] = types.HostLocalIPAMDriver - } - return nil -} diff --git a/libpod/network/cni/config_test.go b/libpod/network/cni/config_test.go deleted file mode 100644 index c2e5fc985..000000000 --- a/libpod/network/cni/config_test.go +++ /dev/null @@ -1,1378 +0,0 @@ -// +build linux - -package cni_test - -import ( - "bytes" - "io/ioutil" - "net" - "os" - "path/filepath" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - gomegaTypes "github.com/onsi/gomega/types" - "github.com/sirupsen/logrus" - - "github.com/containers/podman/v3/libpod/network/types" - "github.com/containers/podman/v3/libpod/network/util" -) - -var _ = Describe("Config", func() { - var ( - libpodNet types.ContainerNetwork - cniConfDir string - logBuffer bytes.Buffer - ) - - BeforeEach(func() { - var err error - cniConfDir, err = ioutil.TempDir("", "podman_cni_test") - if err != nil { - Fail("Failed to create tmpdir") - - } - logBuffer = bytes.Buffer{} - logrus.SetOutput(&logBuffer) - }) - - JustBeforeEach(func() { - var err error - libpodNet, err = getNetworkInterface(cniConfDir, false) - if err != nil { - Fail("Failed to create NewCNINetworkInterface") - } - }) - - AfterEach(func() { - os.RemoveAll(cniConfDir) - }) - - Context("basic network config tests", func() { - - It("check default network config exists", func() { - networks, err := libpodNet.NetworkList() - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - 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).To(BeNil()) - Expect(network2).To(Equal(network1)) - - // inspect by ID - network2, err = libpodNet.NetworkInspect(network1.ID) - Expect(err).To(BeNil()) - Expect(network2).To(Equal(network1)) - - // inspect by partial ID - network2, err = libpodNet.NetworkInspect(network1.ID[:10]) - Expect(err).To(BeNil()) - Expect(network2).To(Equal(network1)) - - // create a new interface to force a config load from disk - libpodNet, err = getNetworkInterface(cniConfDir, false) - Expect(err).To(BeNil()) - - network2, err = libpodNet.NetworkInspect(network1.Name) - Expect(err).To(BeNil()) - Expect(network2).To(Equal(network1)) - - err = libpodNet.NetworkRemove(network1.Name) - Expect(err).To(BeNil()) - 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() { - network := types.Network{} - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.Subnets).To(HaveLen(1)) - - network = types.Network{} - network2, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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("create bridge config", func() { - network := types.Network{Driver: "bridge"} - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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 with same name should fail", func() { - network := types.Network{ - Driver: "bridge", - NetworkInterface: "cni-podman2", - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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) - 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) - Expect(err).To(BeNil()) - 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(HaveLen(0)) - 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) - Expect(err).To(BeNil()) - 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(0)) - 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) - Expect(err).To(BeNil()) - 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"`) - }) - - 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) - Expect(err).To(BeNil()) - 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{ - "mode": mode, - }, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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{ - "mode": "test", - }, - } - _, err := libpodNet.NetworkCreate(network) - 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) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("parent interface idonotexists does not exists")) - }) - - It("create macvlan config with internal should fail", func() { - network := types.Network{ - Driver: "macvlan", - Internal: true, - } - _, err := libpodNet.NetworkCreate(network) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("internal is not supported with macvlan")) - }) - - It("create ipvlan config with mode", func() { - for _, mode := range []string{"l2", "l3", "l3s"} { - network := types.Network{ - Driver: "ipvlan", - Options: map[string]string{ - "mode": mode, - }, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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, false) - Expect(err).To(BeNil()) - - network2, err := libpodNet.NetworkInspect(network1.Name) - Expect(err).To(BeNil()) - Expect(network2).To(Equal(network1)) - } - }) - - It("create ipvlan config with invalid mode", func() { - network := types.Network{ - Driver: "ipvlan", - Options: map[string]string{ - "mode": "test", - }, - } - _, err := libpodNet.NetworkCreate(network) - 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) - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - 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, false) - Expect(err).To(BeNil()) - // check the the networks are identical - network2, err := libpodNet.NetworkInspect(network1.Name) - Expect(err).To(BeNil()) - Expect(network1).To(Equal(network2)) - }) - - It("create bridge with ipv6 enabled", func() { - network := types.Network{ - Driver: "bridge", - IPv6Enabled: true, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - 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) - 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) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - 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) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - 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) - Expect(err).To(BeNil()) - 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, false) - Expect(err).To(BeNil()) - - network1, err = libpodNet.NetworkInspect(network1.Name) - Expect(err).To(BeNil()) - 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) - 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) - 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) - 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) - Expect(err).To(BeNil()) - 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) - Expect(err).To(HaveOccurred()) - }) - - It("create network with name", func() { - name := "myname" - network := types.Network{ - Name: name, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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) - Expect(err).To(HaveOccurred()) - }) - - It("create network with interface name", func() { - name := "myname" - network := types.Network{ - NetworkInterface: name, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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) - Expect(err).To(HaveOccurred()) - }) - - It("create network with ID should fail", func() { - id := "17f29b073143d8cd97b5bbe492bdeffec1c5fee55cc1fe2112c8b9335f8b6121" - network := types.Network{ - ID: id, - } - _, err := libpodNet.NetworkCreate(network) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("ID can not be set for network create")) - }) - - It("create bridge with dns", func() { - network := types.Network{ - Driver: "bridge", - DNSEnabled: true, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - 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()) - }) - - It("create network with labels", func() { - network := types.Network{ - Labels: map[string]string{ - "key": "value", - }, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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{ - "mtu": "1500", - }, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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{ - "mtu": "abc", - }, - } - _, err := libpodNet.NetworkCreate(network) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`parsing "abc": invalid syntax`)) - - network = types.Network{ - Options: map[string]string{ - "mtu": "-1", - }, - } - _, err = libpodNet.NetworkCreate(network) - 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{ - "mtu": "1500", - }, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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{ - "vlan": "5", - }, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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{ - "vlan": "abc", - }, - } - _, err := libpodNet.NetworkCreate(network) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`parsing "abc": invalid syntax`)) - - network = types.Network{ - Options: map[string]string{ - "vlan": "-1", - }, - } - _, err = libpodNet.NetworkCreate(network) - 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) - 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) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("unsupported driver someDriver")) - }) - - It("network create internal and dns", func() { - network := types.Network{ - Driver: "bridge", - Internal: true, - DNSEnabled: true, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - Expect(network1.ID).To(Equal("b44b7426c006839e7fe6f15d1faf64db58079d5233cba09b43be2257c1652cf5")) - network = types.Network{Name: "net5"} - network1, err = libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - Expect(network1.Name).To(Equal("net")) - network = types.Network{Name: "net"} - _, err = libpodNet.NetworkCreate(network) - 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).To(BeNil()) - 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) - Expect(err).To(BeNil()) - Expect(network1.Subnets).To(HaveLen(2)) - network = types.Network{Subnets: []types.Subnet{{Subnet: n}}} - _, err = libpodNet.NetworkCreate(network) - 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) - 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")) - }) - }) - - Context("network load valid existing ones", func() { - - BeforeEach(func() { - dir := "testfiles/valid" - files, err := ioutil.ReadDir(dir) - if err != nil { - Fail("Failed to read test directory") - } - for _, file := range files { - filename := file.Name() - data, err := ioutil.ReadFile(filepath.Join(dir, filename)) - if err != nil { - Fail("Failed to copy test files") - } - err = ioutil.WriteFile(filepath.Join(cniConfDir, filename), data, 0700) - if err != nil { - Fail("Failed to copy test files") - } - } - }) - - It("load networks from disk", func() { - nets, err := libpodNet.NetworkList() - Expect(err).To(BeNil()) - Expect(nets).To(HaveLen(9)) - // test the we do not show logrus warnings/errors - logString := logBuffer.String() - Expect(logString).To(BeEmpty()) - }) - - It("change network struct fields should not affect network struct in the backend", func() { - nets, err := libpodNet.NetworkList() - Expect(err).To(BeNil()) - Expect(nets).To(HaveLen(9)) - - nets[0].Name = "myname" - nets, err = libpodNet.NetworkList() - Expect(err).To(BeNil()) - Expect(nets).To(HaveLen(9)) - Expect(nets).ToNot(ContainElement(HaveNetworkName("myname"))) - - network, err := libpodNet.NetworkInspect("bridge") - Expect(err).To(BeNil()) - network.NetworkInterface = "abc" - - network, err = libpodNet.NetworkInspect("bridge") - Expect(err).To(BeNil()) - Expect(network.NetworkInterface).ToNot(Equal("abc")) - }) - - It("bridge network", func() { - network, err := libpodNet.NetworkInspect("bridge") - Expect(err).To(BeNil()) - 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).To(BeNil()) - 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(HaveLen(0)) - // DHCP - }) - - It("internal network", func() { - network, err := libpodNet.NetworkInspect("internal") - Expect(err).To(BeNil()) - 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).To(BeNil()) - 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).To(BeNil()) - 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(HaveLen(0)) - 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).To(BeNil()) - 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).To(BeNil()) - 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).To(BeNil()) - 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("network list with filters (name)", func() { - filters := map[string][]string{ - "name": {"internal", "bridge"}, - } - filterFuncs, err := util.GenerateNetworkFilters(filters) - Expect(err).To(BeNil()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - Expect(networks).To(HaveLen(9)) - Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"), - HaveNetworkName("mtu"), HaveNetworkName("vlan"), HaveNetworkName("podman"), - HaveNetworkName("label"), HaveNetworkName("macvlan"), HaveNetworkName("macvlan_mtu"), HaveNetworkName("dualstack"))) - }) - - It("network list with filters (label)", func() { - filters := map[string][]string{ - "label": {"mykey"}, - } - filterFuncs, err := util.GenerateNetworkFilters(filters) - Expect(err).To(BeNil()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - networks, err = libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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).To(BeNil()) - Expect(filterFuncs).To(HaveLen(2)) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - networks, err = libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - Expect(networks).To(HaveLen(0)) - }) - - It("create bridge network with used interface name", func() { - network := types.Network{ - NetworkInterface: "cni-podman9", - } - _, err := libpodNet.NetworkCreate(network) - 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 := ioutil.ReadDir(dir) - if err != nil { - Fail("Failed to read test directory") - } - for _, file := range files { - filename := file.Name() - data, err := ioutil.ReadFile(filepath.Join(dir, filename)) - if err != nil { - Fail("Failed to copy test files") - } - err = ioutil.WriteFile(filepath.Join(cniConfDir, filename), data, 0700) - if err != nil { - Fail("Failed to copy test files") - } - } - }) - - It("load invalid networks from disk", func() { - nets, err := libpodNet.NetworkList() - Expect(err).To(BeNil()) - 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 string, match string) { - data, err := ioutil.ReadFile(path) - ExpectWithOffset(1, err).To(BeNil()) - ExpectWithOffset(1, string(data)).To(ContainSubstring(match)) -} - -// 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/libpod/network/cni/network.go b/libpod/network/cni/network.go deleted file mode 100644 index 41e3e414e..000000000 --- a/libpod/network/cni/network.go +++ /dev/null @@ -1,273 +0,0 @@ -// +build linux - -package cni - -import ( - "context" - "crypto/sha256" - "encoding/hex" - "os" - "strings" - "time" - - "github.com/containernetworking/cni/libcni" - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/libpod/network/types" - "github.com/containers/storage/pkg/lockfile" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -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 - - // isMachine describes whenever podman runs in a podman machine environment. - isMachine bool - - // lock is a internal lock for critical operations - lock lockfile.Locker - - // 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 -} - -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 - // CNIPluginDirs is a list of directories where cni should look for the plugins. - CNIPluginDirs []string - - // DefaultNetwork is the name for the default network. - DefaultNetwork string - // DefaultSubnet is the default subnet for the default network. - DefaultSubnet string - - // IsMachine describes whenever podman runs in a podman machine environment. - IsMachine bool - - // LockFile is the path to lock file. - LockFile string -} - -// 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) { - // TODO: consider using a shared memory lock - lock, err := lockfile.GetLockfile(conf.LockFile) - if err != nil { - return nil, err - } - - defaultNetworkName := conf.DefaultNetwork - if defaultNetworkName == "" { - defaultNetworkName = types.DefaultNetworkName - } - - defaultSubnet := conf.DefaultSubnet - if defaultSubnet == "" { - defaultSubnet = types.DefaultSubnet - } - defaultNet, err := types.ParseCIDR(defaultSubnet) - if err != nil { - return nil, errors.Wrap(err, "failed to parse default subnet") - } - - cni := libcni.NewCNIConfig(conf.CNIPluginDirs, &cniExec{}) - n := &cniNetwork{ - cniConfigDir: conf.CNIConfigDir, - cniPluginDirs: conf.CNIPluginDirs, - cniConf: cni, - defaultNetwork: defaultNetworkName, - defaultSubnet: defaultNet, - isMachine: conf.IsMachine, - lock: lock, - } - - 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} -} - -func (n *cniNetwork) loadNetworks() error { - // check the mod time of the config dir - f, err := os.Stat(n.cniConfigDir) - if err != nil { - return err - } - 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 !define.NameRegex.MatchString(conf.Name) { - logrus.Warnf("CNI config list %s has invalid name, skipping: %v", file, define.RegexError) - 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 { - 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 errors.Wrapf(err, "failed to create default network %s", n.defaultNetwork) - } - 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, errors.Errorf("more than one result for network ID %s", nameOrID) - } - net = val - } - } - if net != nil { - return net, nil - } - return nil, errors.Wrapf(define.ErrNoSuchNetwork, "unable to find network with name or ID %s", nameOrID) -} - -// 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 -} - -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/libpod/network/cni/run.go b/libpod/network/cni/run.go deleted file mode 100644 index d0ff49b73..000000000 --- a/libpod/network/cni/run.go +++ /dev/null @@ -1,273 +0,0 @@ -// +build linux - -package cni - -import ( - "context" - "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/containernetworking/plugins/pkg/ns" - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/libpod/network/internal/util" - "github.com/containers/podman/v3/libpod/network/types" - "github.com/hashicorp/go-multierror" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" -) - -// 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 - } - - // set the loopback adapter up in the container netns - err = ns.WithNetNSPath(namespacePath, func(_ ns.NetNS) error { - link, err := netlink.LinkByName("lo") - if err == nil { - err = netlink.LinkSetUp(link) - } - return err - }) - if err != nil { - return nil, errors.Wrapf(err, "failed to set the loopback adapter up") - } - - 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 nil, err - } - - results := make(map[string]types.StatusBlock, len(options.Networks)) - 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 := make(map[string]interface{}) - caps["capabilities"] = map[string]bool{"ips": true} - network.cniNet.Plugins[0], retErr = libcni.InjectConf(network.cniNet.Plugins[0], caps) - if retErr != nil { - return nil, 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 nil, 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 nil, retErr - } - results[name] = status - } - return results, nil -} - -// CNIResultToStatus convert the cni result to status block -// nolint:golint -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, errors.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 _, 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, errors.Errorf("invalid cni result, interface index %d out of range", *ip.Interface) - } - cniInt := cniResult.Interfaces[*ip.Interface] - netInt, ok := interfaces[cniInt.Name] - if ok { - netInt.Subnets = append(netInt.Subnets, types.NetAddress{ - IPNet: types.IPNet{IPNet: ip.Address}, - Gateway: ip.Gateway, - }) - interfaces[cniInt.Name] = netInt - } else { - mac, err := net.ParseMAC(cniInt.Mac) - if err != nil { - return result, err - } - interfaces[cniInt.Name] = types.NetInterface{ - MacAddress: types.HardwareAddr(mac), - Subnets: []types.NetAddress{{ - IPNet: types.IPNet{IPNet: ip.Address}, - Gateway: ip.Gateway, - }}, - } - } - } - 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]interface{}{}, - } - - // Propagate environment CNI_ARGS - for _, kvpairs := range strings.Split(os.Getenv("CNI_ARGS"), ";") { - if keyval := strings.SplitN(kvpairs, "=", 2); len(keyval) == 2 { - rt.Args = append(rt.Args, [2]string{keyval[0], keyval[1]}) - } - } - - // 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 - 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, errors.Wrapf(define.ErrNoSuchNetwork, "network %s", name)) - continue - } - cniConfList = network.cniNet - } - - err = n.cniConf.DelNetworkList(context.Background(), cniConfList, rt) - if err != nil { - 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, errors.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 -} diff --git a/libpod/network/cni/run_test.go b/libpod/network/cni/run_test.go deleted file mode 100644 index f6cc2d412..000000000 --- a/libpod/network/cni/run_test.go +++ /dev/null @@ -1,1329 +0,0 @@ -// +build linux - -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/ioutil" - "net" - "os" - "path/filepath" - "strconv" - "sync" - "time" - - "github.com/containernetworking/plugins/pkg/ns" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" - "golang.org/x/sys/unix" - - "github.com/containers/podman/v3/libpod/network/types" - "github.com/containers/podman/v3/pkg/netns" - "github.com/containers/podman/v3/pkg/rootless" - "github.com/containers/storage/pkg/stringid" -) - -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 mountns to mount a tmpfs to /var/lib/cni. - runTest := func(run func()) { - netNSTest.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - err := os.MkdirAll(cniVarDir, 0755) - Expect(err).To(BeNil(), "Failed to create cniVarDir") - err = unix.Unshare(unix.CLONE_NEWNS) - Expect(err).To(BeNil(), "Failed to create new mountns") - err = unix.Mount("tmpfs", cniVarDir, "tmpfs", unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV, "") - Expect(err).To(BeNil(), "Failed to mount tmpfs for cniVarDir") - defer unix.Unmount(cniVarDir, 0) - - // we have to setup the loopback adapter in this netns to use port forwarding - link, err := netlink.LinkByName("lo") - Expect(err).To(BeNil(), "Failed to get loopback adapter") - err = netlink.LinkSetUp(link) - Expect(err).To(BeNil(), "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 rootless.IsRootless() { - Skip("this test needs to be run as root") - } - - var err error - cniConfDir, err = ioutil.TempDir("", "podman_cni_test") - if err != nil { - Fail("Failed to create tmpdir") - } - logBuffer = bytes.Buffer{} - logrus.SetOutput(&logBuffer) - - netNSTest, err = netns.NewNS() - if err != nil { - Fail("Failed to create netns") - } - - netNSContainer, err = netns.NewNS() - if err != nil { - Fail("Failed to create netns") - } - }) - - JustBeforeEach(func() { - var err error - libpodNet, err = getNetworkInterface(cniConfDir, false) - if err != nil { - Fail("Failed to create NewCNINetworkInterface") - } - }) - - AfterEach(func() { - os.RemoveAll(cniConfDir) - - netns.UnmountNS(netNSTest) - netNSTest.Close() - - netns.UnmountNS(netNSContainer) - netNSContainer.Close() - }) - - 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).To(BeNil()) - 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, false) - Expect(err).To(BeNil()) - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).To(BeNil()) - }) - }) - - 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).To(BeNil()) - 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).To(BeNil()) - }) - }) - - 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).To(BeNil()) - Expect(res).To(HaveLen(1)) - Expect(res).To(HaveKey(defNet)) - Expect(res[defNet].Interfaces).To(HaveKey(intName)) - Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1)) - Expect(res[defNet].Interfaces[intName].Subnets[0].IPNet.IP.String()).To(ContainSubstring("10.88.0.")) - Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) - // default network has no dns - Expect(res[defNet].DNSServerIPs).To(BeEmpty()) - Expect(res[defNet].DNSSearchDomains).To(BeEmpty()) - var wg sync.WaitGroup - wg.Add(1) - // start a listener in the container ns - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - runNetListener(&wg, protocol, "0.0.0.0", 5000, testdata) - return nil - }) - Expect(err).To(BeNil()) - - conn, err := net.Dial(protocol, "127.0.0.1:5000") - Expect(err).To(BeNil()) - _, err = conn.Write([]byte(testdata)) - Expect(err).To(BeNil()) - conn.Close() - - // wait for the listener to finish - wg.Wait() - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).To(BeNil()) - }) - }) - - 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).To(BeNil()) - Expect(res).To(HaveLen(1)) - Expect(res).To(HaveKey(defNet)) - Expect(res[defNet].Interfaces).To(HaveKey(intName)) - Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1)) - containerIP := res[defNet].Interfaces[intName].Subnets[0].IPNet.IP.String() - Expect(containerIP).To(ContainSubstring("10.88.0.")) - Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) - // default network has no dns - Expect(res[defNet].DNSServerIPs).To(BeEmpty()) - Expect(res[defNet].DNSSearchDomains).To(BeEmpty()) - - // loop over all ports - for p := 5001; p < 5004; p++ { - port := p - var wg sync.WaitGroup - wg.Add(1) - testdata := stringid.GenerateNonCryptoID() - // start a listener in the container ns - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - runNetListener(&wg, protocol, containerIP, port-1, testdata) - return nil - }) - Expect(err).To(BeNil()) - - conn, err := net.Dial(protocol, net.JoinHostPort("127.0.0.1", strconv.Itoa(port))) - Expect(err).To(BeNil()) - _, err = conn.Write([]byte(testdata)) - Expect(err).To(BeNil()) - conn.Close() - - // wait for the listener to finish - wg.Wait() - } - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).To(BeNil()) - }) - }) - } - - 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).To(BeNil()) - Expect(res).To(HaveLen(1)) - Expect(res).To(HaveKey(defNet)) - Expect(res[defNet].Interfaces).To(HaveKey(intName)) - Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1)) - Expect(res[defNet].Interfaces[intName].Subnets[0].IPNet.IP.String()).To(ContainSubstring("10.88.0.")) - Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) - - for _, proto := range []string{"tcp", "udp"} { - // copy proto to extra var to keep correct references in the goroutines - protocol := proto - - testdata := stringid.GenerateNonCryptoID() - var wg sync.WaitGroup - wg.Add(1) - // start tcp listener in the container ns - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - runNetListener(&wg, protocol, "0.0.0.0", 5000, testdata) - return nil - }) - Expect(err).To(BeNil()) - - conn, err := net.Dial(protocol, "127.0.0.1:5000") - Expect(err).To(BeNil()) - _, err = conn.Write([]byte(testdata)) - Expect(err).To(BeNil()) - conn.Close() - - // wait for the listener to finish - wg.Wait() - } - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).To(BeNil()) - }) - }) - - It("call setup twice", func() { - runTest(func() { - network := types.Network{} - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - - 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).To(BeNil()) - 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).To(BeNil()) - Expect(i.Name).To(Equal(intName1)) - Expect(i.HardwareAddr).To(Equal((net.HardwareAddr)(macInt1))) - addrs, err := i.Addrs() - Expect(err).To(BeNil()) - 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).To(BeNil()) - 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).To(BeNil()) - - network = types.Network{} - network2, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - - intName2 := "eth1" - netName2 := network2.Name - - setupOpts.Networks = map[string]types.PerNetworkOptions{ - netName2: { - InterfaceName: intName2, - }, - } - - res, err = libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).To(BeNil()) - 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).To(BeNil()) - Expect(i.Name).To(Equal(intName1)) - Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt1))) - addrs, err := i.Addrs() - Expect(err).To(BeNil()) - subnet := &net.IPNet{ - IP: ipInt1, - Mask: net.CIDRMask(24, 32), - } - Expect(addrs).To(ContainElements(subnet)) - - i, err = net.InterfaceByName(intName2) - Expect(err).To(BeNil()) - Expect(i.Name).To(Equal(intName2)) - Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt2))) - addrs, err = i.Addrs() - Expect(err).To(BeNil()) - 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).To(BeNil()) - 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).To(BeNil()) - - 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).To(BeNil()) - 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).To(BeNil()) - 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).To(BeNil()) - - err = libpodNet.NetworkRemove(netName1) - Expect(err).To(BeNil()) - err = libpodNet.NetworkRemove(netName2) - Expect(err).To(BeNil()) - - // 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) - Expect(err).To(BeNil()) - - network = types.Network{ - Subnets: []types.Subnet{ - {Subnet: subnet2}, - }, - } - network2, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - - 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).To(BeNil()) - 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).To(BeNil()) - Expect(i.Name).To(Equal(intName1)) - Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt1))) - addrs, err := i.Addrs() - Expect(err).To(BeNil()) - subnet := &net.IPNet{ - IP: ipInt1, - Mask: net.CIDRMask(24, 32), - } - Expect(addrs).To(ContainElements(subnet)) - - i, err = net.InterfaceByName(intName2) - Expect(err).To(BeNil()) - Expect(i.Name).To(Equal(intName2)) - Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt2))) - addrs, err = i.Addrs() - Expect(err).To(BeNil()) - 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).To(BeNil()) - 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).To(BeNil()) - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).To(BeNil()) - 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).To(BeNil()) - 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).To(BeNil()) - }) - - }) - - 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. - SkipIfNotFedora("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) - Expect(err).To(BeNil()) - - 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).To(BeNil()) - 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).To(BeNil()) - Expect(i.Name).To(Equal(interfaceName)) - Expect(i.HardwareAddr).To(Equal(mac)) - addrs, err := i.Addrs() - Expect(err).To(BeNil()) - 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).To(BeNil()) - 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).To(BeNil()) - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).To(BeNil()) - 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).To(BeNil()) - 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).To(BeNil()) - }) - }) - - 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) - Expect(err).To(BeNil()) - netName := network1.Name - intName := "eth0" - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - netName: { - InterfaceName: intName, - }, - }, - }, - } - - os.Setenv("CNI_ARGS", "IP="+ip) - defer os.Unsetenv("CNI_ARGS") - - res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).To(BeNil()) - 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).To(BeNil()) - Expect(i.Name).To(Equal(intName)) - addrs, err := i.Addrs() - Expect(err).To(BeNil()) - 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).To(BeNil()) - 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).To(BeNil()) - }) - }) - }) - - Context("network setup test with networks from disk", func() { - - BeforeEach(func() { - dir := "testfiles/valid" - files, err := ioutil.ReadDir(dir) - if err != nil { - Fail("Failed to read test directory") - } - for _, file := range files { - filename := file.Name() - data, err := ioutil.ReadFile(filepath.Join(dir, filename)) - if err != nil { - Fail("Failed to copy test files") - } - err = ioutil.WriteFile(filepath.Join(cniConfDir, filename), data, 0700) - 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. - SkipIfNotFedora("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).To(BeNil()) - 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)) - res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) - Expect(err).To(BeNil()) - 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).To(BeNil()) - Expect(i.Name).To(Equal(interfaceName)) - addrs, err := i.Addrs() - Expect(err).To(BeNil()) - 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).To(BeNil()) - 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).To(BeNil()) - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).To(BeNil()) - 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).To(BeNil()) - 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).To(BeNil()) - }) - }) - - 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) - Expect(err).To(BeNil()) - - subnet2, _ := types.ParseCIDR("192.168.1.0/31") - network = types.Network{ - Subnets: []types.Subnet{ - {Subnet: subnet2}, - }, - } - network2, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - - 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).To(BeNil()) - 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).To(BeNil()) - }) - }) - - 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) - Expect(err).To(BeNil()) - - 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).To(BeNil()) - 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).To(BeNil()) - // make sure to read in a separate goroutine to not block - go func() { - defer GinkgoRecover() - defer wg.Done() - conn, err := ln.Accept() - Expect(err).To(BeNil()) - conn.SetDeadline(time.Now().Add(1 * time.Second)) - data, err := ioutil.ReadAll(conn) - Expect(err).To(BeNil()) - 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).To(BeNil()) - conn.SetDeadline(time.Now().Add(1 * time.Second)) - go func() { - defer GinkgoRecover() - defer wg.Done() - data := make([]byte, len(expectedData)) - i, err := conn.Read(data) - Expect(err).To(BeNil()) - Expect(i).To(Equal(len(expectedData))) - Expect(string(data)).To(Equal(expectedData)) - conn.Close() - }() - default: - Fail("unsupported protocol") - } -} diff --git a/libpod/network/cni/testfiles/invalid/broken.conflist b/libpod/network/cni/testfiles/invalid/broken.conflist deleted file mode 100644 index e5bf48b39..000000000 --- a/libpod/network/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/libpod/network/cni/testfiles/invalid/invalid_gateway.conflist b/libpod/network/cni/testfiles/invalid/invalid_gateway.conflist deleted file mode 100644 index f03c1fde4..000000000 --- a/libpod/network/cni/testfiles/invalid/invalid_gateway.conflist +++ /dev/null @@ -1,51 +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" - }, - { - "type": "dnsname", - "domainName": "dns.podman", - "capabilities": { - "aliases": true - } - } - ] -} diff --git a/libpod/network/cni/testfiles/invalid/invalidname.conflist b/libpod/network/cni/testfiles/invalid/invalidname.conflist deleted file mode 100644 index e35be69db..000000000 --- a/libpod/network/cni/testfiles/invalid/invalidname.conflist +++ /dev/null @@ -1,49 +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" - }, - { - "type": "dnsname", - "domainName": "dns.podman", - "capabilities": { - "aliases": true - } - } - ] -} diff --git a/libpod/network/cni/testfiles/invalid/noname.conflist b/libpod/network/cni/testfiles/invalid/noname.conflist deleted file mode 100644 index 865abadf8..000000000 --- a/libpod/network/cni/testfiles/invalid/noname.conflist +++ /dev/null @@ -1,48 +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" - }, - { - "type": "dnsname", - "domainName": "dns.podman", - "capabilities": { - "aliases": true - } - } - ] -} diff --git a/libpod/network/cni/testfiles/invalid/noplugin.conflist b/libpod/network/cni/testfiles/invalid/noplugin.conflist deleted file mode 100644 index af192adca..000000000 --- a/libpod/network/cni/testfiles/invalid/noplugin.conflist +++ /dev/null @@ -1,5 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "bridge", - "plugins": [] -} diff --git a/libpod/network/cni/testfiles/invalid/samename1.conflist b/libpod/network/cni/testfiles/invalid/samename1.conflist deleted file mode 100644 index 57b325264..000000000 --- a/libpod/network/cni/testfiles/invalid/samename1.conflist +++ /dev/null @@ -1,49 +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" - }, - { - "type": "dnsname", - "domainName": "dns.podman", - "capabilities": { - "aliases": true - } - } - ] -} diff --git a/libpod/network/cni/testfiles/invalid/samename2.conflist b/libpod/network/cni/testfiles/invalid/samename2.conflist deleted file mode 100644 index 57b325264..000000000 --- a/libpod/network/cni/testfiles/invalid/samename2.conflist +++ /dev/null @@ -1,49 +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" - }, - { - "type": "dnsname", - "domainName": "dns.podman", - "capabilities": { - "aliases": true - } - } - ] -} diff --git a/libpod/network/cni/testfiles/valid/87-podman.conflist b/libpod/network/cni/testfiles/valid/87-podman.conflist deleted file mode 100644 index ef760a61b..000000000 --- a/libpod/network/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/libpod/network/cni/testfiles/valid/bridge.conflist b/libpod/network/cni/testfiles/valid/bridge.conflist deleted file mode 100644 index 8952b50b7..000000000 --- a/libpod/network/cni/testfiles/valid/bridge.conflist +++ /dev/null @@ -1,51 +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" - }, - { - "type": "dnsname", - "domainName": "dns.podman", - "capabilities": { - "aliases": true - } - } - ] -} diff --git a/libpod/network/cni/testfiles/valid/dualstack.conflist b/libpod/network/cni/testfiles/valid/dualstack.conflist deleted file mode 100644 index dd08382f0..000000000 --- a/libpod/network/cni/testfiles/valid/dualstack.conflist +++ /dev/null @@ -1,58 +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" - }, - { - "type": "dnsname", - "domainName": "dns.podman", - "capabilities": { - "aliases": true - } - } - ] -} diff --git a/libpod/network/cni/testfiles/valid/internal.conflist b/libpod/network/cni/testfiles/valid/internal.conflist deleted file mode 100644 index 1b6f15a96..000000000 --- a/libpod/network/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/libpod/network/cni/testfiles/valid/label.conflist b/libpod/network/cni/testfiles/valid/label.conflist deleted file mode 100644 index 1501f9bd7..000000000 --- a/libpod/network/cni/testfiles/valid/label.conflist +++ /dev/null @@ -1,54 +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" - }, - { - "type": "dnsname", - "domainName": "dns.podman", - "capabilities": { - "aliases": true - } - } - ] -} diff --git a/libpod/network/cni/testfiles/valid/macvlan.conflist b/libpod/network/cni/testfiles/valid/macvlan.conflist deleted file mode 100644 index 8f3692334..000000000 --- a/libpod/network/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/libpod/network/cni/testfiles/valid/macvlan_mtu.conflist b/libpod/network/cni/testfiles/valid/macvlan_mtu.conflist deleted file mode 100644 index 2fd259117..000000000 --- a/libpod/network/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/libpod/network/cni/testfiles/valid/mtu.conflist b/libpod/network/cni/testfiles/valid/mtu.conflist deleted file mode 100644 index db5f7e194..000000000 --- a/libpod/network/cni/testfiles/valid/mtu.conflist +++ /dev/null @@ -1,49 +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" - }, - { - "type": "dnsname", - "domainName": "dns.podman", - "capabilities": { - "aliases": true - } - } - ] -} diff --git a/libpod/network/cni/testfiles/valid/vlan.conflist b/libpod/network/cni/testfiles/valid/vlan.conflist deleted file mode 100644 index 75e8967f1..000000000 --- a/libpod/network/cni/testfiles/valid/vlan.conflist +++ /dev/null @@ -1,50 +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" - }, - { - "type": "dnsname", - "domainName": "dns.podman", - "capabilities": { - "aliases": true - } - } - ] -} diff --git a/libpod/network/internal/util/bridge.go b/libpod/network/internal/util/bridge.go deleted file mode 100644 index 476557050..000000000 --- a/libpod/network/internal/util/bridge.go +++ /dev/null @@ -1,69 +0,0 @@ -package util - -import ( - "net" - - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/libpod/network/types" - "github.com/containers/podman/v3/libpod/network/util" - pkgutil "github.com/containers/podman/v3/pkg/util" - "github.com/pkg/errors" -) - -func CreateBridge(n NetUtil, network *types.Network, usedNetworks []*net.IPNet) error { - if network.NetworkInterface != "" { - bridges := GetBridgeInterfaceNames(n) - if pkgutil.StringInSlice(network.NetworkInterface, bridges) { - return errors.Errorf("bridge name %s already in use", network.NetworkInterface) - } - if !define.NameRegex.MatchString(network.NetworkInterface) { - return errors.Wrapf(define.RegexError, "bridge name %s invalid", network.NetworkInterface) - } - } else { - var err error - network.NetworkInterface, err = GetFreeDeviceName(n) - if err != nil { - return err - } - } - - if network.IPAMOptions["driver"] != types.DHCPIPAMDriver { - if len(network.Subnets) == 0 { - freeSubnet, err := GetFreeIPv4NetworkSubnet(usedNetworks) - if err != nil { - return err - } - network.Subnets = append(network.Subnets, *freeSubnet) - } - // ipv6 enabled means dual stack, check if we already have - // a ipv4 or ipv6 subnet and add one if not. - if network.IPv6Enabled { - ipv4 := false - ipv6 := false - for _, subnet := range network.Subnets { - if util.IsIPv6(subnet.Subnet.IP) { - ipv6 = true - } - if util.IsIPv4(subnet.Subnet.IP) { - ipv4 = true - } - } - if !ipv4 { - freeSubnet, err := GetFreeIPv4NetworkSubnet(usedNetworks) - if err != nil { - return err - } - network.Subnets = append(network.Subnets, *freeSubnet) - } - if !ipv6 { - freeSubnet, err := GetFreeIPv6NetworkSubnet(usedNetworks) - if err != nil { - return err - } - network.Subnets = append(network.Subnets, *freeSubnet) - } - } - network.IPAMOptions["driver"] = types.HostLocalIPAMDriver - } - return nil -} diff --git a/libpod/network/internal/util/create.go b/libpod/network/internal/util/create.go deleted file mode 100644 index cecfd7133..000000000 --- a/libpod/network/internal/util/create.go +++ /dev/null @@ -1,42 +0,0 @@ -package util - -import ( - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/libpod/network/types" - "github.com/pkg/errors" -) - -func CommonNetworkCreate(n NetUtil, network *types.Network) error { - if network.Labels == nil { - network.Labels = map[string]string{} - } - if network.Options == nil { - network.Options = map[string]string{} - } - if network.IPAMOptions == nil { - network.IPAMOptions = map[string]string{} - } - - var name string - var err error - // validate the name when given - if network.Name != "" { - if !define.NameRegex.MatchString(network.Name) { - return errors.Wrapf(define.RegexError, "network name %s invalid", network.Name) - } - if _, err := n.Network(network.Name); err == nil { - return errors.Wrapf(define.ErrNetworkExists, "network name %s already used", network.Name) - } - } else { - name, err = GetFreeDeviceName(n) - if err != nil { - return err - } - network.Name = name - // also use the name as interface name when we create a bridge network - if network.Driver == types.BridgeNetworkDriver && network.NetworkInterface == "" { - network.NetworkInterface = name - } - } - return nil -} diff --git a/libpod/network/internal/util/interface.go b/libpod/network/internal/util/interface.go deleted file mode 100644 index 4b01a09b8..000000000 --- a/libpod/network/internal/util/interface.go +++ /dev/null @@ -1,19 +0,0 @@ -package util - -import "github.com/containers/podman/v3/libpod/network/types" - -// This is a helper package to allow code sharing between the different -// network interfaces. - -// NetUtil is a helper interface which all network interfaces should implement to allow easy code sharing -type NetUtil interface { - // ForEach eaxecutes the given function for each network - ForEach(func(types.Network)) - // Len returns the number of networks - Len() int - // DefaultInterfaceName return the default interface name, this will be suffixed by a number - DefaultInterfaceName() string - // Network returns the network with the given name or ID. - // It returns an error if the network is not found - Network(nameOrID string) (*types.Network, error) -} diff --git a/libpod/network/internal/util/interfaces.go b/libpod/network/internal/util/interfaces.go deleted file mode 100644 index 20819f756..000000000 --- a/libpod/network/internal/util/interfaces.go +++ /dev/null @@ -1,34 +0,0 @@ -package util - -import "net" - -// getLiveNetworkSubnets returns a slice of subnets representing what the system -// has defined as network interfaces -func getLiveNetworkSubnets() ([]*net.IPNet, error) { - addrs, err := net.InterfaceAddrs() - if err != nil { - return nil, err - } - nets := make([]*net.IPNet, 0, len(addrs)) - for _, address := range addrs { - _, n, err := net.ParseCIDR(address.String()) - if err != nil { - return nil, err - } - nets = append(nets, n) - } - return nets, nil -} - -// GetLiveNetworkNames returns a list of network interface names on the system -func GetLiveNetworkNames() ([]string, error) { - liveInterfaces, err := net.Interfaces() - if err != nil { - return nil, err - } - interfaceNames := make([]string, 0, len(liveInterfaces)) - for _, i := range liveInterfaces { - interfaceNames = append(interfaceNames, i.Name) - } - return interfaceNames, nil -} diff --git a/libpod/network/internal/util/ip.go b/libpod/network/internal/util/ip.go deleted file mode 100644 index 7fe35d3d4..000000000 --- a/libpod/network/internal/util/ip.go +++ /dev/null @@ -1,70 +0,0 @@ -package util - -import ( - "crypto/rand" - "net" - - "github.com/pkg/errors" -) - -func incByte(subnet *net.IPNet, idx int, shift uint) error { - if idx < 0 { - return errors.New("no more subnets left") - } - if subnet.IP[idx] == 255 { - subnet.IP[idx] = 0 - return incByte(subnet, idx-1, 0) - } - subnet.IP[idx] += 1 << shift - return nil -} - -// NextSubnet returns subnet incremented by 1 -func NextSubnet(subnet *net.IPNet) (*net.IPNet, error) { - newSubnet := &net.IPNet{ - IP: subnet.IP, - Mask: subnet.Mask, - } - ones, bits := newSubnet.Mask.Size() - if ones == 0 { - return nil, errors.Errorf("%s has only one subnet", subnet.String()) - } - zeroes := uint(bits - ones) - shift := zeroes % 8 - idx := ones/8 - 1 - if idx < 0 { - idx = 0 - } - if err := incByte(newSubnet, idx, shift); err != nil { - return nil, err - } - return newSubnet, nil -} - -func NetworkIntersectsWithNetworks(n *net.IPNet, networklist []*net.IPNet) bool { - for _, nw := range networklist { - if networkIntersect(n, nw) { - return true - } - } - return false -} - -func networkIntersect(n1, n2 *net.IPNet) bool { - return n2.Contains(n1.IP) || n1.Contains(n2.IP) -} - -// getRandomIPv6Subnet returns a random internal ipv6 subnet as described in RFC3879. -func getRandomIPv6Subnet() (net.IPNet, error) { - ip := make(net.IP, 8, net.IPv6len) - // read 8 random bytes - _, err := rand.Read(ip) - if err != nil { - return net.IPNet{}, nil - } - // first byte must be FD as per RFC3879 - ip[0] = 0xfd - // add 8 zero bytes - ip = append(ip, make([]byte, 8)...) - return net.IPNet{IP: ip, Mask: net.CIDRMask(64, 128)}, nil -} diff --git a/libpod/network/internal/util/ip_test.go b/libpod/network/internal/util/ip_test.go deleted file mode 100644 index eaed769d7..000000000 --- a/libpod/network/internal/util/ip_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package util - -import ( - "fmt" - "net" - "reflect" - "testing" -) - -func parseCIDR(n string) *net.IPNet { - _, parsedNet, _ := net.ParseCIDR(n) - return parsedNet -} - -func TestNextSubnet(t *testing.T) { - type args struct { - subnet *net.IPNet - } - tests := []struct { - name string - args args - want *net.IPNet - wantErr bool - }{ - {"class b", args{subnet: parseCIDR("192.168.0.0/16")}, parseCIDR("192.169.0.0/16"), false}, - {"class c", args{subnet: parseCIDR("192.168.1.0/24")}, parseCIDR("192.168.2.0/24"), false}, - } - for _, tt := range tests { - test := tt - t.Run(test.name, func(t *testing.T) { - got, err := NextSubnet(test.args.subnet) - if (err != nil) != test.wantErr { - t.Errorf("NextSubnet() error = %v, wantErr %v", err, test.wantErr) - return - } - if !reflect.DeepEqual(got, test.want) { - t.Errorf("NextSubnet() got = %v, want %v", got, test.want) - } - }) - } -} - -func TestGetRandomIPv6Subnet(t *testing.T) { - for i := 0; i < 1000; i++ { - t.Run(fmt.Sprintf("GetRandomIPv6Subnet %d", i), func(t *testing.T) { - sub, err := getRandomIPv6Subnet() - if err != nil { - t.Errorf("GetRandomIPv6Subnet() error should be nil: %v", err) - return - } - if sub.IP.To4() != nil { - t.Errorf("ip %s is not an ipv6 address", sub.IP) - } - if sub.IP[0] != 0xfd { - t.Errorf("ipv6 %s does not start with fd", sub.IP) - } - ones, bytes := sub.Mask.Size() - if ones != 64 || bytes != 128 { - t.Errorf("wrong network mask %v, it should be /64", sub.Mask) - } - }) - } -} diff --git a/libpod/network/internal/util/parse.go b/libpod/network/internal/util/parse.go deleted file mode 100644 index 1f68df0bb..000000000 --- a/libpod/network/internal/util/parse.go +++ /dev/null @@ -1,37 +0,0 @@ -package util - -import ( - "strconv" - - "github.com/pkg/errors" -) - -// ParseMTU parses the mtu option -func ParseMTU(mtu string) (int, error) { - if mtu == "" { - return 0, nil // default - } - m, err := strconv.Atoi(mtu) - if err != nil { - return 0, err - } - if m < 0 { - return 0, errors.Errorf("mtu %d is less than zero", m) - } - return m, nil -} - -// ParseVlan parses the vlan option -func ParseVlan(vlan string) (int, error) { - if vlan == "" { - return 0, nil // default - } - v, err := strconv.Atoi(vlan) - if err != nil { - return 0, err - } - if v < 0 || v > 4094 { - return 0, errors.Errorf("vlan ID %d must be between 0 and 4094", v) - } - return v, nil -} diff --git a/libpod/network/internal/util/util.go b/libpod/network/internal/util/util.go deleted file mode 100644 index d9b9a8dc0..000000000 --- a/libpod/network/internal/util/util.go +++ /dev/null @@ -1,123 +0,0 @@ -package util - -import ( - "errors" - "fmt" - "net" - - "github.com/containers/podman/v3/libpod/network/types" - "github.com/containers/podman/v3/pkg/util" - "github.com/sirupsen/logrus" -) - -// GetBridgeInterfaceNames returns all bridge interface names -// already used by network configs -func GetBridgeInterfaceNames(n NetUtil) []string { - names := make([]string, 0, n.Len()) - n.ForEach(func(net types.Network) { - if net.Driver == types.BridgeNetworkDriver { - names = append(names, net.NetworkInterface) - } - }) - return names -} - -// GetUsedNetworkNames returns all network names already used -// by network configs -func GetUsedNetworkNames(n NetUtil) []string { - names := make([]string, 0, n.Len()) - n.ForEach(func(net types.Network) { - if net.Driver == types.BridgeNetworkDriver { - names = append(names, net.NetworkInterface) - } - }) - return names -} - -// GetFreeDeviceName returns a free device name which can -// be used for new configs as name and bridge interface name. -// The base name is suffixed by a number -func GetFreeDeviceName(n NetUtil) (string, error) { - bridgeNames := GetBridgeInterfaceNames(n) - netNames := GetUsedNetworkNames(n) - liveInterfaces, err := GetLiveNetworkNames() - if err != nil { - return "", nil - } - names := make([]string, 0, len(bridgeNames)+len(netNames)+len(liveInterfaces)) - names = append(names, bridgeNames...) - names = append(names, netNames...) - names = append(names, liveInterfaces...) - // FIXME: Is a limit fine? - // Start by 1, 0 is reserved for the default network - for i := 1; i < 1000000; i++ { - deviceName := fmt.Sprintf("%s%d", n.DefaultInterfaceName(), i) - if !util.StringInSlice(deviceName, names) { - logrus.Debugf("found free device name %s", deviceName) - return deviceName, nil - } - } - return "", errors.New("could not find free device name, to many iterations") -} - -// GetUsedSubnets returns a list of all used subnets by network -// configs and interfaces on the host. -func GetUsedSubnets(n NetUtil) ([]*net.IPNet, error) { - // first, load all used subnets from network configs - subnets := make([]*net.IPNet, 0, n.Len()) - n.ForEach(func(n types.Network) { - for i := range n.Subnets { - subnets = append(subnets, &n.Subnets[i].Subnet.IPNet) - } - }) - // second, load networks from the current system - liveSubnets, err := getLiveNetworkSubnets() - if err != nil { - return nil, err - } - return append(subnets, liveSubnets...), nil -} - -// GetFreeIPv4NetworkSubnet returns a unused ipv4 subnet -func GetFreeIPv4NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) { - // the default podman network is 10.88.0.0/16 - // start locking for free /24 networks - network := &net.IPNet{ - IP: net.IP{10, 89, 0, 0}, - Mask: net.IPMask{255, 255, 255, 0}, - } - - // TODO: make sure to not use public subnets - for { - if intersectsConfig := NetworkIntersectsWithNetworks(network, usedNetworks); !intersectsConfig { - logrus.Debugf("found free ipv4 network subnet %s", network.String()) - return &types.Subnet{ - Subnet: types.IPNet{IPNet: *network}, - }, nil - } - var err error - network, err = NextSubnet(network) - if err != nil { - return nil, err - } - } -} - -// GetFreeIPv6NetworkSubnet returns a unused ipv6 subnet -func GetFreeIPv6NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) { - // FIXME: Is 10000 fine as limit? We should prevent an endless loop. - for i := 0; i < 10000; i++ { - // RFC4193: Choose the ipv6 subnet random and NOT sequentially. - network, err := getRandomIPv6Subnet() - if err != nil { - return nil, err - } - if intersectsConfig := NetworkIntersectsWithNetworks(&network, usedNetworks); !intersectsConfig { - logrus.Debugf("found free ipv6 network subnet %s", network.String()) - return &types.Subnet{ - Subnet: types.IPNet{IPNet: network}, - }, nil - } - } - return nil, errors.New("failed to get random ipv6 subnet") -} diff --git a/libpod/network/internal/util/validate.go b/libpod/network/internal/util/validate.go deleted file mode 100644 index 62c3f3951..000000000 --- a/libpod/network/internal/util/validate.go +++ /dev/null @@ -1,121 +0,0 @@ -package util - -import ( - "net" - - "github.com/containers/podman/v3/libpod/network/types" - "github.com/containers/podman/v3/libpod/network/util" - "github.com/pkg/errors" -) - -// ValidateSubnet will validate a given Subnet. It checks if the -// given gateway and lease range are part of this subnet. If the -// gateway is empty and addGateway is true it will get the first -// available ip in the subnet assigned. -func ValidateSubnet(s *types.Subnet, addGateway bool, usedNetworks []*net.IPNet) error { - if s == nil { - return errors.New("subnet is nil") - } - if s.Subnet.IP == nil { - return errors.New("subnet ip is nil") - } - - // Reparse to ensure subnet is valid. - // Do not use types.ParseCIDR() because we want the ip to be - // the network address and not a random ip in the subnet. - _, net, err := net.ParseCIDR(s.Subnet.String()) - if err != nil { - return errors.Wrap(err, "subnet invalid") - } - - // check that the new subnet does not conflict with existing ones - if NetworkIntersectsWithNetworks(net, usedNetworks) { - return errors.Errorf("subnet %s is already used on the host or by another config", net.String()) - } - - s.Subnet = types.IPNet{IPNet: *net} - if s.Gateway != nil { - if !s.Subnet.Contains(s.Gateway) { - return errors.Errorf("gateway %s not in subnet %s", s.Gateway, &s.Subnet) - } - util.NormalizeIP(&s.Gateway) - } else if addGateway { - ip, err := util.FirstIPInSubnet(net) - if err != nil { - return err - } - s.Gateway = ip - } - - if s.LeaseRange != nil { - if s.LeaseRange.StartIP != nil { - if !s.Subnet.Contains(s.LeaseRange.StartIP) { - return errors.Errorf("lease range start ip %s not in subnet %s", s.LeaseRange.StartIP, &s.Subnet) - } - util.NormalizeIP(&s.LeaseRange.StartIP) - } - if s.LeaseRange.EndIP != nil { - if !s.Subnet.Contains(s.LeaseRange.EndIP) { - return errors.Errorf("lease range end ip %s not in subnet %s", s.LeaseRange.EndIP, &s.Subnet) - } - util.NormalizeIP(&s.LeaseRange.EndIP) - } - } - return nil -} - -// ValidateSubnets will validate the subnets for this network. -// It also sets the gateway if the gateway is empty and it sets -// IPv6Enabled to true if at least one subnet is ipv6. -func ValidateSubnets(network *types.Network, usedNetworks []*net.IPNet) error { - for i := range network.Subnets { - err := ValidateSubnet(&network.Subnets[i], !network.Internal, usedNetworks) - if err != nil { - return err - } - if util.IsIPv6(network.Subnets[i].Subnet.IP) { - network.IPv6Enabled = true - } - } - return nil -} - -func ValidateSetupOptions(n NetUtil, namespacePath string, options types.SetupOptions) error { - if namespacePath == "" { - return errors.New("namespacePath is empty") - } - if options.ContainerID == "" { - return errors.New("ContainerID is empty") - } - if len(options.Networks) == 0 { - return errors.New("must specify at least one network") - } - for name, netOpts := range options.Networks { - network, err := n.Network(name) - if err != nil { - return err - } - err = validatePerNetworkOpts(network, netOpts) - if err != nil { - return err - } - } - return nil -} - -// validatePerNetworkOpts checks that all given static ips are in a subnet on this network -func validatePerNetworkOpts(network *types.Network, netOpts types.PerNetworkOptions) error { - if netOpts.InterfaceName == "" { - return errors.Errorf("interface name on network %s is empty", network.Name) - } -outer: - for _, ip := range netOpts.StaticIPs { - for _, s := range network.Subnets { - if s.Subnet.Contains(ip) { - continue outer - } - } - return errors.Errorf("requested static ip %s not in any subnet on network %s", ip.String(), network.Name) - } - return nil -} diff --git a/libpod/network/netavark/config.go b/libpod/network/netavark/config.go deleted file mode 100644 index 5cab76710..000000000 --- a/libpod/network/netavark/config.go +++ /dev/null @@ -1,210 +0,0 @@ -// +build linux - -package netavark - -import ( - "encoding/json" - "net" - "os" - "path/filepath" - "time" - - "github.com/containers/podman/v3/libpod/define" - internalutil "github.com/containers/podman/v3/libpod/network/internal/util" - "github.com/containers/podman/v3/libpod/network/types" - "github.com/containers/storage/pkg/stringid" - "github.com/pkg/errors" -) - -// NetworkCreate will take a partial filled Network and fill the -// missing fields. It creates the Network and returns the full Network. -func (n *netavarkNetwork) NetworkCreate(net types.Network) (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 { - return types.Network{}, err - } - // add the new network to the map - n.networks[network.Name] = network - return *network, nil -} - -func (n *netavarkNetwork) networkCreate(newNetwork types.Network, defaultNet bool) (*types.Network, error) { - // if no driver is set use the default one - if newNetwork.Driver == "" { - newNetwork.Driver = types.DefaultNetworkDriver - } - if !defaultNet { - // 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, errors.Wrap(define.ErrInvalidArg, "ID can not be set for network create") - } - - // generate random network ID - var i int - for i = 0; i < 1000; i++ { - id := stringid.GenerateNonCryptoID() - if _, err := n.getNetwork(id); err != nil { - newNetwork.ID = id - break - } - } - if i == 1000 { - return nil, errors.New("failed to create random network ID") - } - } - - err := internalutil.CommonNetworkCreate(n, &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 { - usedNetworks, err = internalutil.GetUsedSubnets(n) - if err != nil { - return nil, err - } - } - - switch newNetwork.Driver { - case types.BridgeNetworkDriver: - err = internalutil.CreateBridge(n, &newNetwork, usedNetworks) - if err != nil { - return nil, err - } - // validate the given options, we do not need them but just check to make sure they are valid - for key, value := range newNetwork.Options { - switch key { - case "mtu": - _, err = internalutil.ParseMTU(value) - if err != nil { - return nil, err - } - - case "vlan": - _, err = internalutil.ParseVlan(value) - if err != nil { - return nil, err - } - - default: - return nil, errors.Errorf("unsupported network option %s", key) - } - } - - default: - return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported driver %s", newNetwork.Driver) - } - - err = internalutil.ValidateSubnets(&newNetwork, usedNetworks) - if err != nil { - return nil, err - } - - // FIXME: If we have a working solution for internal networks with dns this check should be removed. - if newNetwork.DNSEnabled && newNetwork.Internal { - return nil, errors.New("cannot set internal and dns enabled") - } - - newNetwork.Created = time.Now() - - if !defaultNet { - confPath := filepath.Join(n.networkConfigDir, newNetwork.Name+".json") - f, err := os.Create(confPath) - if err != nil { - return nil, err - } - enc := json.NewEncoder(f) - enc.SetIndent("", " ") - err = enc.Encode(newNetwork) - if err != nil { - return nil, err - } - } - - return &newNetwork, nil -} - -// NetworkRemove will remove the Network with the given name or ID. -// It does not ensure that the network is unused. -func (n *netavarkNetwork) 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.Name == n.defaultNetwork { - return errors.Errorf("default network %s cannot be removed", n.defaultNetwork) - } - - file := filepath.Join(n.networkConfigDir, network.Name+".json") - // make sure to not error for ErrNotExist - if err := os.Remove(file); err != nil && !errors.Is(err, os.ErrNotExist) { - return err - } - delete(n.networks, network.Name) - 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 *netavarkNetwork) 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) { - continue outer - } - } - networks = append(networks, *net) - } - return networks, nil -} - -// NetworkInspect will return the Network with the given name or ID. -func (n *netavarkNetwork) 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, nil -} diff --git a/libpod/network/netavark/config_test.go b/libpod/network/netavark/config_test.go deleted file mode 100644 index ee4a825f1..000000000 --- a/libpod/network/netavark/config_test.go +++ /dev/null @@ -1,1123 +0,0 @@ -// +build linux - -package netavark_test - -import ( - "bytes" - "io/ioutil" - "net" - "os" - "path/filepath" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - gomegaTypes "github.com/onsi/gomega/types" - "github.com/sirupsen/logrus" - - "github.com/containers/podman/v3/libpod/network/types" - "github.com/containers/podman/v3/libpod/network/util" -) - -var _ = Describe("Config", func() { - var ( - libpodNet types.ContainerNetwork - networkConfDir string - logBuffer bytes.Buffer - ) - - BeforeEach(func() { - var err error - networkConfDir, err = ioutil.TempDir("", "podman_netavark_test") - if err != nil { - Fail("Failed to create tmpdir") - - } - logBuffer = bytes.Buffer{} - logrus.SetOutput(&logBuffer) - }) - - JustBeforeEach(func() { - var err error - libpodNet, err = getNetworkInterface(networkConfDir, false) - if err != nil { - Fail("Failed to create NewCNINetworkInterface") - } - }) - - AfterEach(func() { - os.RemoveAll(networkConfDir) - }) - - Context("basic network config tests", func() { - - It("check default network config exists", func() { - networks, err := libpodNet.NetworkList() - Expect(err).To(BeNil()) - Expect(networks).To(HaveLen(1)) - Expect(networks[0].Name).To(Equal("podman")) - Expect(networks[0].Driver).To(Equal("bridge")) - Expect(networks[0].ID).To(Equal("2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9")) - Expect(networks[0].NetworkInterface).To(Equal("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) - Expect(err).To(BeNil()) - Expect(network1.Name).ToNot(BeEmpty()) - path := filepath.Join(networkConfDir, network1.Name+".json") - 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).To(BeNil()) - EqualNetwork(network2, network1) - - // inspect by ID - network2, err = libpodNet.NetworkInspect(network1.ID) - Expect(err).To(BeNil()) - EqualNetwork(network2, network1) - - // inspect by partial ID - network2, err = libpodNet.NetworkInspect(network1.ID[:10]) - Expect(err).To(BeNil()) - EqualNetwork(network2, network1) - - // create a new interface to force a config load from disk - libpodNet, err = getNetworkInterface(networkConfDir, false) - Expect(err).To(BeNil()) - - network2, err = libpodNet.NetworkInspect(network1.Name) - Expect(err).To(BeNil()) - EqualNetwork(network2, network1) - - err = libpodNet.NetworkRemove(network1.Name) - Expect(err).To(BeNil()) - 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() { - network := types.Network{} - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.Subnets).To(HaveLen(1)) - - network = types.Network{} - network2, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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("create bridge config", func() { - network := types.Network{Driver: "bridge"} - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(filepath.Join(networkConfDir, network1.Name+".json")).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 with same name should fail", func() { - network := types.Network{ - Driver: "bridge", - NetworkInterface: "podman2", - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(network1.ID).ToNot(BeEmpty()) - Expect(network1.NetworkInterface).To(Equal("podman2")) - Expect(network1.Driver).To(Equal("bridge")) - - _, err = libpodNet.NetworkCreate(network) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("bridge name podman2 already in use")) - }) - - 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) - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - 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(networkConfDir, false) - Expect(err).To(BeNil()) - // check the the networks are identical - network2, err := libpodNet.NetworkInspect(network1.Name) - Expect(err).To(BeNil()) - EqualNetwork(network2, network1) - }) - - It("create bridge with ipv6 enabled", func() { - network := types.Network{ - Driver: "bridge", - IPv6Enabled: true, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - 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) - Expect(err).To(BeNil()) - 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) - 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) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - endIP := "10.0.0.10" - network = types.Network{ - Driver: "bridge", - Subnets: []types.Subnet{ - {Subnet: n, LeaseRange: &types.LeaseRange{ - EndIP: net.ParseIP(endIP), - }}, - }, - } - network1, err = libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - Expect(network1.Name).ToNot(BeEmpty()) - Expect(filepath.Join(networkConfDir, network1.Name+".json")).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).To(BeNil()) - - 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) - Expect(err).To(BeNil()) - 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) - 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) - 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) - 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) - Expect(err).To(BeNil()) - 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) - Expect(err).To(HaveOccurred()) - }) - - It("create network with name", func() { - name := "myname" - network := types.Network{ - Name: name, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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) - Expect(err).To(HaveOccurred()) - }) - - It("create network with interface name", func() { - name := "myname" - network := types.Network{ - NetworkInterface: name, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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) - Expect(err).To(HaveOccurred()) - }) - - It("create network with ID should fail", func() { - id := "17f29b073143d8cd97b5bbe492bdeffec1c5fee55cc1fe2112c8b9335f8b6121" - network := types.Network{ - ID: id, - } - _, err := libpodNet.NetworkCreate(network) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("ID can not be set for network create")) - }) - - It("create bridge with dns", func() { - network := types.Network{ - Driver: "bridge", - DNSEnabled: true, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.DNSEnabled).To(BeTrue()) - path := filepath.Join(networkConfDir, network1.Name+".json") - Expect(path).To(BeARegularFile()) - grepInFile(path, `"dns_enabled": true`) - }) - - It("create bridge with internal", func() { - network := types.Network{ - Driver: "bridge", - Internal: true, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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()) - }) - - It("create network with labels", func() { - network := types.Network{ - Labels: map[string]string{ - "key": "value", - }, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - 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{ - "mtu": "1500", - }, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Options).ToNot(BeNil()) - path := filepath.Join(networkConfDir, network1.Name+".json") - 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{ - "mtu": "abc", - }, - } - _, err := libpodNet.NetworkCreate(network) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`parsing "abc": invalid syntax`)) - - network = types.Network{ - Options: map[string]string{ - "mtu": "-1", - }, - } - _, err = libpodNet.NetworkCreate(network) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`mtu -1 is less than zero`)) - }) - - It("create network with vlan option", func() { - network := types.Network{ - Options: map[string]string{ - "vlan": "5", - }, - } - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - Expect(network1.Driver).To(Equal("bridge")) - Expect(network1.Options).ToNot(BeNil()) - path := filepath.Join(networkConfDir, network1.Name+".json") - 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{ - "vlan": "abc", - }, - } - _, err := libpodNet.NetworkCreate(network) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`parsing "abc": invalid syntax`)) - - network = types.Network{ - Options: map[string]string{ - "vlan": "-1", - }, - } - _, err = libpodNet.NetworkCreate(network) - 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) - 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) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("unsupported driver someDriver")) - }) - - It("network create internal and dns", func() { - network := types.Network{ - Driver: "bridge", - Internal: true, - DNSEnabled: true, - } - _, err := libpodNet.NetworkCreate(network) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("cannot set internal and dns enabled")) - }) - - It("network inspect partial ID", func() { - network := types.Network{Name: "net4"} - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - Expect(network1.ID).To(HaveLen(64)) - - network2, err := libpodNet.NetworkInspect(network1.ID[:10]) - Expect(err).ToNot(HaveOccurred()) - EqualNetwork(network2, network1) - }) - - It("network create two with same name", func() { - network := types.Network{Name: "net"} - network1, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - Expect(network1.Name).To(Equal("net")) - network = types.Network{Name: "net"} - _, err = libpodNet.NetworkCreate(network) - 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).To(BeNil()) - 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) - Expect(err).To(BeNil()) - Expect(network1.Subnets).To(HaveLen(2)) - network = types.Network{Subnets: []types.Subnet{{Subnet: n}}} - _, err = libpodNet.NetworkCreate(network) - 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) - 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")) - }) - }) - - Context("network load valid existing ones", func() { - - BeforeEach(func() { - dir := "testfiles/valid" - files, err := ioutil.ReadDir(dir) - if err != nil { - Fail("Failed to read test directory") - } - for _, file := range files { - filename := file.Name() - data, err := ioutil.ReadFile(filepath.Join(dir, filename)) - if err != nil { - Fail("Failed to copy test files") - } - err = ioutil.WriteFile(filepath.Join(networkConfDir, filename), data, 0700) - if err != nil { - Fail("Failed to copy test files") - } - } - }) - - It("load networks from disk", func() { - nets, err := libpodNet.NetworkList() - Expect(err).To(BeNil()) - Expect(nets).To(HaveLen(7)) - // test the we do not show logrus warnings/errors - logString := logBuffer.String() - Expect(logString).To(BeEmpty()) - }) - - It("change network struct fields should not affect network struct in the backend", func() { - nets, err := libpodNet.NetworkList() - Expect(err).To(BeNil()) - Expect(nets).To(HaveLen(7)) - - nets[0].Name = "myname" - nets, err = libpodNet.NetworkList() - Expect(err).To(BeNil()) - Expect(nets).To(HaveLen(7)) - Expect(nets).ToNot(ContainElement(HaveNetworkName("myname"))) - - network, err := libpodNet.NetworkInspect("bridge") - Expect(err).To(BeNil()) - network.NetworkInterface = "abc" - - network, err = libpodNet.NetworkInspect("bridge") - Expect(err).To(BeNil()) - Expect(network.NetworkInterface).ToNot(Equal("abc")) - }) - - It("bridge network", func() { - network, err := libpodNet.NetworkInspect("bridge") - Expect(err).To(BeNil()) - Expect(network.Name).To(Equal("bridge")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.NetworkInterface).To(Equal("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("internal network", func() { - network, err := libpodNet.NetworkInspect("internal") - Expect(err).To(BeNil()) - Expect(network.Name).To(Equal("internal")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.NetworkInterface).To(Equal("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).To(BeNil()) - Expect(network.Name).To(Equal("mtu")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.NetworkInterface).To(Equal("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("bridge network with vlan", func() { - network, err := libpodNet.NetworkInspect("vlan") - Expect(err).To(BeNil()) - Expect(network.Name).To(Equal("vlan")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.NetworkInterface).To(Equal("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).To(BeNil()) - Expect(network.Name).To(Equal("label")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.NetworkInterface).To(Equal("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).To(BeNil()) - Expect(network.Name).To(Equal("dualstack")) - Expect(network.ID).To(HaveLen(64)) - Expect(network.NetworkInterface).To(Equal("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("network list with filters (name)", func() { - filters := map[string][]string{ - "name": {"internal", "bridge"}, - } - filterFuncs, err := util.GenerateNetworkFilters(filters) - Expect(err).To(BeNil()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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"}, - } - filterFuncs, err := util.GenerateNetworkFilters(filters) - Expect(err).To(BeNil()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - Expect(networks).To(HaveLen(7)) - Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"), - HaveNetworkName("mtu"), HaveNetworkName("vlan"), HaveNetworkName("podman"), - HaveNetworkName("label"), HaveNetworkName("dualstack"))) - }) - - It("network list with filters (label)", func() { - filters := map[string][]string{ - "label": {"mykey"}, - } - filterFuncs, err := util.GenerateNetworkFilters(filters) - Expect(err).To(BeNil()) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - networks, err = libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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).To(BeNil()) - Expect(filterFuncs).To(HaveLen(2)) - - networks, err := libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - 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).To(BeNil()) - - networks, err = libpodNet.NetworkList(filterFuncs...) - Expect(err).To(BeNil()) - Expect(networks).To(HaveLen(0)) - }) - - It("create bridge network with used interface name", func() { - network := types.Network{ - NetworkInterface: "podman9", - } - _, err := libpodNet.NetworkCreate(network) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("bridge name podman9 already in use")) - }) - }) - - Context("network load invalid existing ones", func() { - - BeforeEach(func() { - dir := "testfiles/invalid" - files, err := ioutil.ReadDir(dir) - if err != nil { - Fail("Failed to read test directory") - } - for _, file := range files { - filename := file.Name() - data, err := ioutil.ReadFile(filepath.Join(dir, filename)) - if err != nil { - Fail("Failed to copy test files") - } - err = ioutil.WriteFile(filepath.Join(networkConfDir, filename), data, 0700) - if err != nil { - Fail("Failed to copy test files") - } - } - }) - - It("load invalid networks from disk", func() { - nets, err := libpodNet.NetworkList() - Expect(err).To(BeNil()) - Expect(nets).To(HaveLen(1)) - logString := logBuffer.String() - Expect(logString).To(ContainSubstring("Error reading network config file \\\"%s/broken.json\\\": unexpected EOF", networkConfDir)) - Expect(logString).To(ContainSubstring("Network config \\\"%s/invalid name.json\\\" has invalid name: \\\"invalid name\\\", skipping: names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*: invalid argument", networkConfDir)) - Expect(logString).To(ContainSubstring("Network config name \\\"name_miss\\\" does not match file name \\\"name_missmatch.json\\\", skipping")) - Expect(logString).To(ContainSubstring("Network config \\\"%s/wrongID.json\\\" could not be parsed, skipping: invalid network ID \\\"someID\\\"", networkConfDir)) - Expect(logString).To(ContainSubstring("Network config \\\"%s/invalid_gateway.json\\\" could not be parsed, skipping: gateway 10.89.100.1 not in subnet 10.89.9.0/24", networkConfDir)) - }) - - }) - -}) - -func grepInFile(path string, match string) { - data, err := ioutil.ReadFile(path) - ExpectWithOffset(1, err).To(BeNil()) - ExpectWithOffset(1, string(data)).To(ContainSubstring(match)) -} - -// 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)) -} - -// EqualNetwork must be used because comparing the time with deep equal does not work -func EqualNetwork(net1, net2 types.Network) { - ExpectWithOffset(1, net1.Created.Equal(net2.Created)).To(BeTrue(), "net1 created: %v is not equal net2 created: %v", net1.Created, net2.Created) - net1.Created = time.Time{} - net2.Created = time.Time{} - ExpectWithOffset(1, net1).To(Equal(net2)) -} diff --git a/libpod/network/netavark/const.go b/libpod/network/netavark/const.go deleted file mode 100644 index 9709315c6..000000000 --- a/libpod/network/netavark/const.go +++ /dev/null @@ -1,5 +0,0 @@ -// +build linux - -package netavark - -const defaultBridgeName = "podman" diff --git a/libpod/network/netavark/exec.go b/libpod/network/netavark/exec.go deleted file mode 100644 index 01dea8489..000000000 --- a/libpod/network/netavark/exec.go +++ /dev/null @@ -1,159 +0,0 @@ -package netavark - -import ( - "encoding/json" - "errors" - "io" - "os" - "os/exec" - "strconv" - - "github.com/sirupsen/logrus" -) - -type netavarkError struct { - exitCode int - // Set the json key to "error" so we can directly unmarshal into this struct - Msg string `json:"error"` - err error -} - -func (e *netavarkError) Error() string { - ec := "" - // only add the exit code the the error message if we have at least info log level - // the normal user does not need to care about the number - if e.exitCode > 0 && logrus.IsLevelEnabled(logrus.InfoLevel) { - ec = " (exit code " + strconv.Itoa(e.exitCode) + ")" - } - msg := "netavark" + ec - if len(msg) > 0 { - msg += ": " + e.Msg - } - if e.err != nil { - msg += ": " + e.err.Error() - } - return msg -} - -func (e *netavarkError) Unwrap() error { - return e.err -} - -func newNetavarkError(msg string, err error) error { - return &netavarkError{ - Msg: msg, - err: err, - } -} - -// Type to implement io.Writer interface -// This will write the logrus at info level -type logrusNetavarkWriter struct{} - -func (l *logrusNetavarkWriter) Write(b []byte) (int, error) { - logrus.Info("netavark: ", string(b)) - return len(b), nil -} - -// getRustLogEnv returns the RUST_LOG env var based on the current logrus level -func getRustLogEnv() string { - level := logrus.GetLevel().String() - // rust env_log uses warn instead of warning - if level == "warning" { - level = "warn" - } - // the rust netlink library is very verbose - // make sure to only log netavark logs - return "RUST_LOG=netavark=" + level -} - -// execNetavark will execute netavark with the following arguments -// It takes the path to the binary, the list of args and an interface which is -// marshaled to json and send via stdin to netavark. The result interface is -// used to marshal the netavark output into it. This can be nil. -// All errors return by this function should be of the type netavarkError -// to provide a helpful error message. -func (n *netavarkNetwork) execNetavark(args []string, stdin, result interface{}) error { - stdinR, stdinW, err := os.Pipe() - if err != nil { - return newNetavarkError("failed to create stdin pipe", err) - } - stdinWClosed := false - defer func() { - stdinR.Close() - if !stdinWClosed { - stdinW.Close() - } - }() - - stdoutR, stdoutW, err := os.Pipe() - if err != nil { - return newNetavarkError("failed to create stdout pipe", err) - } - stdoutWClosed := false - defer func() { - stdoutR.Close() - if !stdoutWClosed { - stdoutW.Close() - } - }() - - // connect stderr to the podman stderr for logging - var logWriter io.Writer = os.Stderr - if n.syslog { - // connect logrus to stderr as well so that the logs will be written to the syslog as well - logWriter = io.MultiWriter(logWriter, &logrusNetavarkWriter{}) - } - - cmd := exec.Command(n.netavarkBinary, args...) - // connect the pipes to stdin and stdout - cmd.Stdin = stdinR - cmd.Stdout = stdoutW - cmd.Stderr = logWriter - // set the netavark log level to the same as the podman - cmd.Env = append(os.Environ(), getRustLogEnv()) - // if we run with debug log level lets also set RUST_BACKTRACE=1 so we can get the full stack trace in case of panics - if logrus.IsLevelEnabled(logrus.DebugLevel) { - cmd.Env = append(cmd.Env, "RUST_BACKTRACE=1") - } - - err = cmd.Start() - if err != nil { - return newNetavarkError("failed to start process", err) - } - err = json.NewEncoder(stdinW).Encode(stdin) - // we have to close stdinW so netavark gets the EOF and does not hang forever - stdinW.Close() - stdinWClosed = true - if err != nil { - return newNetavarkError("failed to encode stdin data", err) - } - - dec := json.NewDecoder(stdoutR) - - err = cmd.Wait() - // we have to close stdoutW so we can decode the json without hanging forever - stdoutW.Close() - stdoutWClosed = true - if err != nil { - exitError := &exec.ExitError{} - if errors.As(err, &exitError) { - ne := &netavarkError{} - // lets disallow unknown fields to make sure we do not get some unexpected stuff - dec.DisallowUnknownFields() - // this will unmarshal the error message into the error struct - ne.err = dec.Decode(ne) - ne.exitCode = exitError.ExitCode() - return ne - } - return newNetavarkError("unexpected failure during execution", err) - } - - if result != nil { - err = dec.Decode(result) - if err != nil { - return newNetavarkError("failed to decode result", err) - } - } - return nil -} diff --git a/libpod/network/netavark/ipam.go b/libpod/network/netavark/ipam.go deleted file mode 100644 index db46ee652..000000000 --- a/libpod/network/netavark/ipam.go +++ /dev/null @@ -1,368 +0,0 @@ -package netavark - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/containers/podman/v3/libpod/network/types" - "github.com/containers/podman/v3/libpod/network/util" - "github.com/pkg/errors" - "go.etcd.io/bbolt" -) - -// IPAM boltdb structure -// Each network has their own bucket with the network name as bucket key. -// Inside the network bucket there is an ID bucket which maps the container ID (key) -// to a json array of ip addresses (value). -// The network bucket also has a bucket for each subnet, the subnet is used as key. -// Inside the subnet bucket an ip is used as key and the container ID as value. - -const ( - idBucket = "ids" - // lastIP this is used as key to store the last allocated ip - // note that this string should not be 4 or 16 byte long - lastIP = "lastIP" -) - -var ( - idBucketKey = []byte(idBucket) - lastIPKey = []byte(lastIP) -) - -type ipamError struct { - msg string - cause error -} - -func (e *ipamError) Error() string { - msg := "IPAM error" - if e.msg != "" { - msg += ": " + e.msg - } - if e.cause != nil { - msg += ": " + e.cause.Error() - } - return msg -} - -func newIPAMError(cause error, msg string, args ...interface{}) *ipamError { - return &ipamError{ - msg: fmt.Sprintf(msg, args...), - cause: cause, - } -} - -// openDB will open the ipam database -// Note that the caller has to Close it. -func (n *netavarkNetwork) openDB() (*bbolt.DB, error) { - db, err := bbolt.Open(n.ipamDBPath, 0600, nil) - if err != nil { - return nil, newIPAMError(err, "failed to open database %s", n.ipamDBPath) - } - return db, nil -} - -// allocIPs will allocate ips for the the container. It will change the -// NetworkOptions in place. When static ips are given it will validate -// that these are free to use and will allocate them to the container. -func (n *netavarkNetwork) allocIPs(opts *types.NetworkOptions) error { - db, err := n.openDB() - if err != nil { - return err - } - defer db.Close() - - err = db.Update(func(tx *bbolt.Tx) error { - for netName, netOpts := range opts.Networks { - network := n.networks[netName] - if network == nil { - return newIPAMError(nil, "could not find network %q", netName) - } - - // check if we have to alloc ips - if !requiresIPAMAlloc(network) { - continue - } - - // create/get network bucket - netBkt, err := tx.CreateBucketIfNotExists([]byte(netName)) - if err != nil { - return newIPAMError(err, "failed to create/get network bucket for network %s", netName) - } - - // requestIPs is the list of ips which should be used for this container - requestIPs := make([]net.IP, 0, len(network.Subnets)) - - for _, subnet := range network.Subnets { - subnetBkt, err := netBkt.CreateBucketIfNotExists([]byte(subnet.Subnet.String())) - if err != nil { - return newIPAMError(err, "failed to create/get subnet bucket for network %s", netName) - } - - // search for a static ip which matches the current subnet - // in this case the user wants this one and we should not assign a free one - var ip net.IP - for _, staticIP := range netOpts.StaticIPs { - if subnet.Subnet.Contains(staticIP) { - ip = staticIP - break - } - } - - // when static ip is requested for this network - if ip != nil { - // convert to 4 byte ipv4 if needed - util.NormalizeIP(&ip) - id := subnetBkt.Get(ip) - if id != nil { - return newIPAMError(nil, "requested ip address %s is already allocated to container ID %s", ip.String(), string(id)) - } - } else { - ip, err = getFreeIPFromBucket(subnetBkt, subnet) - if err != nil { - return err - } - err = subnetBkt.Put(lastIPKey, ip) - if err != nil { - return newIPAMError(err, "failed to store last ip in database") - } - } - - err = subnetBkt.Put(ip, []byte(opts.ContainerID)) - if err != nil { - return newIPAMError(err, "failed to store ip in database") - } - - requestIPs = append(requestIPs, ip) - } - - idsBucket, err := netBkt.CreateBucketIfNotExists(idBucketKey) - if err != nil { - return newIPAMError(err, "failed to create/get id bucket for network %s", netName) - } - - ipsBytes, err := json.Marshal(requestIPs) - if err != nil { - return newIPAMError(err, "failed to marshal ips") - } - - err = idsBucket.Put([]byte(opts.ContainerID), ipsBytes) - if err != nil { - return newIPAMError(err, "failed to store ips in database") - } - - netOpts.StaticIPs = requestIPs - opts.Networks[netName] = netOpts - } - return nil - }) - return err -} - -func getFreeIPFromBucket(bucket *bbolt.Bucket, subnet types.Subnet) (net.IP, error) { - var rangeStart net.IP - var rangeEnd net.IP - if subnet.LeaseRange != nil { - rangeStart = subnet.LeaseRange.StartIP - rangeEnd = subnet.LeaseRange.EndIP - } - - if rangeStart == nil { - // let start with the first ip in subnet - rangeStart = util.NextIP(subnet.Subnet.IP) - } - - if rangeEnd == nil { - lastIP, err := util.LastIPInSubnet(&subnet.Subnet.IPNet) - // this error should never happen but lets check anyways to prevent panics - if err != nil { - return nil, errors.Wrap(err, "failed to get lastIP") - } - // ipv4 uses the last ip in a subnet for broadcast so we cannot use it - if util.IsIPv4(lastIP) { - lastIP = util.PrevIP(lastIP) - } - rangeEnd = lastIP - } - - lastIPByte := bucket.Get(lastIPKey) - curIP := net.IP(lastIPByte) - if curIP == nil { - curIP = rangeStart - } else { - curIP = util.NextIP(curIP) - } - - // store the start ip to make sure we know when we looped over all available ips - startIP := curIP - - for { - // skip the gateway - if subnet.Gateway != nil { - if util.Cmp(curIP, subnet.Gateway) == 0 { - curIP = util.NextIP(curIP) - continue - } - } - - // if we are at the end we need to jump back to the start ip - if util.Cmp(curIP, rangeEnd) > 0 { - if util.Cmp(rangeStart, startIP) == 0 { - return nil, newIPAMError(nil, "failed to find free IP in range: %s - %s", rangeStart.String(), rangeEnd.String()) - } - curIP = rangeStart - continue - } - - // check if ip is already used by another container - // if not return it - if bucket.Get(curIP) == nil { - return curIP, nil - } - - curIP = util.NextIP(curIP) - - if util.Cmp(curIP, startIP) == 0 { - return nil, newIPAMError(nil, "failed to find free IP in range: %s - %s", rangeStart.String(), rangeEnd.String()) - } - } -} - -// getAssignedIPs will read the ipam database and will fill in the used ips for this container. -// It will change the NetworkOptions in place. -func (n *netavarkNetwork) getAssignedIPs(opts *types.NetworkOptions) error { - db, err := n.openDB() - if err != nil { - return err - } - defer db.Close() - - err = db.View(func(tx *bbolt.Tx) error { - for netName, netOpts := range opts.Networks { - network := n.networks[netName] - if network == nil { - return newIPAMError(nil, "could not find network %q", netName) - } - - // check if we have to alloc ips - if !requiresIPAMAlloc(network) { - continue - } - // get network bucket - netBkt := tx.Bucket([]byte(netName)) - if netBkt == nil { - return newIPAMError(nil, "failed to get network bucket for network %s", netName) - } - - idBkt := netBkt.Bucket(idBucketKey) - if idBkt == nil { - return newIPAMError(nil, "failed to get id bucket for network %s", netName) - } - - ipJSON := idBkt.Get([]byte(opts.ContainerID)) - if ipJSON == nil { - return newIPAMError(nil, "failed to get ips for container ID %s on network %s", opts.ContainerID, netName) - } - - // assignedIPs is the list of ips which should be used for this container - assignedIPs := make([]net.IP, 0, len(network.Subnets)) - - err = json.Unmarshal(ipJSON, &assignedIPs) - if err != nil { - return newIPAMError(err, "failed to unmarshal ips from database") - } - - for i := range assignedIPs { - util.NormalizeIP(&assignedIPs[i]) - } - - netOpts.StaticIPs = assignedIPs - opts.Networks[netName] = netOpts - } - return nil - }) - return err -} - -// deallocIPs will release the ips in the network options from the DB so that -// they can be reused by other containers. It expects that the network options -// are already filled with the correct ips. Use getAssignedIPs() for this. -func (n *netavarkNetwork) deallocIPs(opts *types.NetworkOptions) error { - db, err := n.openDB() - if err != nil { - return err - } - defer db.Close() - - err = db.Update(func(tx *bbolt.Tx) error { - for netName, netOpts := range opts.Networks { - network := n.networks[netName] - if network == nil { - return newIPAMError(nil, "could not find network %q", netName) - } - - // check if we have to alloc ips - if !requiresIPAMAlloc(network) { - continue - } - // get network bucket - netBkt := tx.Bucket([]byte(netName)) - if netBkt == nil { - return newIPAMError(nil, "failed to get network bucket for network %s", netName) - } - - for _, subnet := range network.Subnets { - subnetBkt := netBkt.Bucket([]byte(subnet.Subnet.String())) - if subnetBkt == nil { - return newIPAMError(nil, "failed to get subnet bucket for network %s", netName) - } - - // search for a static ip which matches the current subnet - // in this case the user wants this one and we should not assign a free one - var ip net.IP - for _, staticIP := range netOpts.StaticIPs { - if subnet.Subnet.Contains(staticIP) { - ip = staticIP - break - } - } - if ip == nil { - return newIPAMError(nil, "failed to find ip for subnet %s on network %s", subnet.Subnet.String(), netName) - } - util.NormalizeIP(&ip) - - err = subnetBkt.Delete(ip) - if err != nil { - return newIPAMError(err, "failed to remove ip %s from subnet bucket for network %s", ip.String(), netName) - } - } - - idBkt := netBkt.Bucket(idBucketKey) - if idBkt == nil { - return newIPAMError(nil, "failed to get id bucket for network %s", netName) - } - - err = idBkt.Delete([]byte(opts.ContainerID)) - if err != nil { - return newIPAMError(err, "failed to remove allocated ips for container ID %s on network %s", opts.ContainerID, netName) - } - } - return nil - }) - return err -} - -// requiresIPAMAlloc return true when we have to allocate ips for this network -// it checks the ipam driver and if subnets are set -func requiresIPAMAlloc(network *types.Network) bool { - // only do host allocation when driver is set to HostLocalIPAMDriver or unset - switch network.IPAMOptions["driver"] { - case "", types.HostLocalIPAMDriver: - default: - return false - } - - // no subnets == no ips to assign - return len(network.Subnets) > 0 -} diff --git a/libpod/network/netavark/ipam_test.go b/libpod/network/netavark/ipam_test.go deleted file mode 100644 index 4b3947501..000000000 --- a/libpod/network/netavark/ipam_test.go +++ /dev/null @@ -1,433 +0,0 @@ -package netavark - -import ( - "bytes" - "fmt" - "io/ioutil" - "net" - "os" - "path/filepath" - - "github.com/containers/podman/v3/libpod/network/types" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/sirupsen/logrus" -) - -var _ = Describe("IPAM", func() { - var ( - networkInterface *netavarkNetwork - networkConfDir string - logBuffer bytes.Buffer - ) - - BeforeEach(func() { - var err error - networkConfDir, err = ioutil.TempDir("", "podman_netavark_test") - if err != nil { - Fail("Failed to create tmpdir") - - } - logBuffer = bytes.Buffer{} - logrus.SetOutput(&logBuffer) - }) - - JustBeforeEach(func() { - libpodNet, err := NewNetworkInterface(InitConfig{ - NetworkConfigDir: networkConfDir, - IPAMDBPath: filepath.Join(networkConfDir, "ipam.db"), - LockFile: filepath.Join(networkConfDir, "netavark.lock"), - }) - if err != nil { - Fail("Failed to create NewCNINetworkInterface") - } - - networkInterface = libpodNet.(*netavarkNetwork) - // run network list to force a network load - networkInterface.NetworkList() - }) - - AfterEach(func() { - os.RemoveAll(networkConfDir) - }) - - It("simple ipam alloc", func() { - netName := types.DefaultNetworkName - for i := 2; i < 100; i++ { - opts := &types.NetworkOptions{ - ContainerID: "someContainerID", - Networks: map[string]types.PerNetworkOptions{ - netName: {}, - }, - } - - err := networkInterface.allocIPs(opts) - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName)) - Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1)) - Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP(fmt.Sprintf("10.88.0.%d", i)).To4())) - } - }) - - It("ipam try to alloc same ip", func() { - netName := types.DefaultNetworkName - opts := &types.NetworkOptions{ - ContainerID: "someContainerID", - Networks: map[string]types.PerNetworkOptions{ - netName: {}, - }, - } - - err := networkInterface.allocIPs(opts) - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName)) - Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1)) - Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP("10.88.0.2").To4())) - - opts = &types.NetworkOptions{ - ContainerID: "otherID", - Networks: map[string]types.PerNetworkOptions{ - netName: {StaticIPs: []net.IP{net.ParseIP("10.88.0.2")}}, - }, - } - err = networkInterface.allocIPs(opts) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("IPAM error: requested ip address 10.88.0.2 is already allocated to container ID someContainerID")) - }) - - It("ipam try to alloc more ips as in range", func() { - s, _ := types.ParseCIDR("10.0.0.1/24") - network, err := networkInterface.NetworkCreate(types.Network{ - Subnets: []types.Subnet{ - { - Subnet: s, - LeaseRange: &types.LeaseRange{ - StartIP: net.ParseIP("10.0.0.10"), - EndIP: net.ParseIP("10.0.0.20"), - }, - }, - }, - }) - Expect(err).ToNot(HaveOccurred()) - - netName := network.Name - - for i := 10; i < 21; i++ { - opts := &types.NetworkOptions{ - ContainerID: fmt.Sprintf("someContainerID-%d", i), - Networks: map[string]types.PerNetworkOptions{ - netName: {}, - }, - } - - err = networkInterface.allocIPs(opts) - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName)) - Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1)) - Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP(fmt.Sprintf("10.0.0.%d", i)).To4())) - } - - opts := &types.NetworkOptions{ - ContainerID: "someContainerID-22", - Networks: map[string]types.PerNetworkOptions{ - netName: {}, - }, - } - - // now this should fail because all free ips are already assigned - err = networkInterface.allocIPs(opts) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("IPAM error: failed to find free IP in range: 10.0.0.10 - 10.0.0.20")) - }) - - It("ipam basic setup", func() { - netName := types.DefaultNetworkName - opts := &types.NetworkOptions{ - ContainerID: "someContainerID", - Networks: map[string]types.PerNetworkOptions{ - netName: {}, - }, - } - - expectedIP := net.ParseIP("10.88.0.2").To4() - - err := networkInterface.allocIPs(opts) - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName)) - Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1)) - Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(expectedIP)) - - // remove static ips from opts - netOpts := opts.Networks[netName] - netOpts.StaticIPs = nil - opts.Networks[netName] = netOpts - - err = networkInterface.getAssignedIPs(opts) - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName)) - Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1)) - Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(expectedIP)) - - err = networkInterface.allocIPs(opts) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("IPAM error: requested ip address 10.88.0.2 is already allocated to container ID someContainerID")) - - // dealloc the ip - err = networkInterface.deallocIPs(opts) - Expect(err).ToNot(HaveOccurred()) - - err = networkInterface.allocIPs(opts) - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName)) - Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1)) - Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(expectedIP)) - }) - - It("ipam dual stack", func() { - s1, _ := types.ParseCIDR("10.0.0.0/26") - s2, _ := types.ParseCIDR("fd80::/24") - network, err := networkInterface.NetworkCreate(types.Network{ - Subnets: []types.Subnet{ - { - Subnet: s1, - }, - { - Subnet: s2, - }, - }, - }) - Expect(err).ToNot(HaveOccurred()) - - netName := network.Name - - opts := &types.NetworkOptions{ - ContainerID: "someContainerID", - Networks: map[string]types.PerNetworkOptions{ - netName: {}, - }, - } - - err = networkInterface.allocIPs(opts) - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName)) - Expect(opts.Networks[netName].StaticIPs).To(HaveLen(2)) - Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4())) - Expect(opts.Networks[netName].StaticIPs[1]).To(Equal(net.ParseIP("fd80::2"))) - - // remove static ips from opts - netOpts := opts.Networks[netName] - netOpts.StaticIPs = nil - opts.Networks[netName] = netOpts - - err = networkInterface.getAssignedIPs(opts) - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName)) - Expect(opts.Networks[netName].StaticIPs).To(HaveLen(2)) - Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4())) - Expect(opts.Networks[netName].StaticIPs[1]).To(Equal(net.ParseIP("fd80::2"))) - - err = networkInterface.deallocIPs(opts) - Expect(err).ToNot(HaveOccurred()) - - // try to alloc the same again - err = networkInterface.allocIPs(opts) - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName)) - Expect(opts.Networks[netName].StaticIPs).To(HaveLen(2)) - Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4())) - Expect(opts.Networks[netName].StaticIPs[1]).To(Equal(net.ParseIP("fd80::2"))) - }) - - It("ipam with two networks", func() { - s, _ := types.ParseCIDR("10.0.0.0/24") - network, err := networkInterface.NetworkCreate(types.Network{ - Subnets: []types.Subnet{ - { - Subnet: s, - }, - }, - }) - Expect(err).ToNot(HaveOccurred()) - - netName1 := network.Name - - s, _ = types.ParseCIDR("10.0.1.0/24") - network, err = networkInterface.NetworkCreate(types.Network{ - Subnets: []types.Subnet{ - { - Subnet: s, - }, - }, - }) - Expect(err).ToNot(HaveOccurred()) - - netName2 := network.Name - - opts := &types.NetworkOptions{ - ContainerID: "someContainerID", - Networks: map[string]types.PerNetworkOptions{ - netName1: {}, - netName2: {}, - }, - } - - err = networkInterface.allocIPs(opts) - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName1)) - Expect(opts.Networks[netName1].StaticIPs).To(HaveLen(1)) - Expect(opts.Networks[netName1].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4())) - Expect(opts.Networks).To(HaveKey(netName2)) - Expect(opts.Networks[netName2].StaticIPs).To(HaveLen(1)) - Expect(opts.Networks[netName2].StaticIPs[0]).To(Equal(net.ParseIP("10.0.1.2").To4())) - - // remove static ips from opts - netOpts := opts.Networks[netName1] - netOpts.StaticIPs = nil - opts.Networks[netName1] = netOpts - netOpts = opts.Networks[netName2] - netOpts.StaticIPs = nil - opts.Networks[netName2] = netOpts - - err = networkInterface.getAssignedIPs(opts) - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName1)) - Expect(opts.Networks[netName1].StaticIPs).To(HaveLen(1)) - Expect(opts.Networks[netName1].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4())) - Expect(opts.Networks).To(HaveKey(netName2)) - Expect(opts.Networks[netName2].StaticIPs).To(HaveLen(1)) - Expect(opts.Networks[netName2].StaticIPs[0]).To(Equal(net.ParseIP("10.0.1.2").To4())) - - err = networkInterface.deallocIPs(opts) - Expect(err).ToNot(HaveOccurred()) - - // try to alloc the same again - err = networkInterface.allocIPs(opts) - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName1)) - Expect(opts.Networks[netName1].StaticIPs).To(HaveLen(1)) - Expect(opts.Networks[netName1].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4())) - Expect(opts.Networks).To(HaveKey(netName2)) - Expect(opts.Networks[netName2].StaticIPs).To(HaveLen(1)) - Expect(opts.Networks[netName2].StaticIPs[0]).To(Equal(net.ParseIP("10.0.1.2").To4())) - }) - - It("ipam alloc more ips as in subnet", func() { - s, _ := types.ParseCIDR("10.0.0.0/26") - network, err := networkInterface.NetworkCreate(types.Network{ - Subnets: []types.Subnet{ - { - Subnet: s, - }, - }, - }) - Expect(err).ToNot(HaveOccurred()) - - netName := network.Name - - for i := 2; i < 64; i++ { - opts := &types.NetworkOptions{ - ContainerID: fmt.Sprintf("id-%d", i), - Networks: map[string]types.PerNetworkOptions{ - netName: {}, - }, - } - err = networkInterface.allocIPs(opts) - if i < 63 { - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName)) - Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1)) - Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP(fmt.Sprintf("10.0.0.%d", i)).To4())) - } else { - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("IPAM error: failed to find free IP in range: 10.0.0.1 - 10.0.0.62")) - } - } - }) - - It("ipam alloc -> dealloc -> alloc", func() { - s, _ := types.ParseCIDR("10.0.0.0/27") - network, err := networkInterface.NetworkCreate(types.Network{ - Subnets: []types.Subnet{ - { - Subnet: s, - }, - }, - }) - Expect(err).ToNot(HaveOccurred()) - - netName := network.Name - - for i := 2; i < 10; i++ { - opts := types.NetworkOptions{ - ContainerID: fmt.Sprintf("id-%d", i), - Networks: map[string]types.PerNetworkOptions{ - netName: {}, - }, - } - err = networkInterface.allocIPs(&opts) - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName)) - Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1)) - Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP(fmt.Sprintf("10.0.0.%d", i)).To4())) - - err = networkInterface.deallocIPs(&opts) - Expect(err).ToNot(HaveOccurred()) - } - - for i := 0; i < 30; i++ { - opts := types.NetworkOptions{ - ContainerID: fmt.Sprintf("id-%d", i), - Networks: map[string]types.PerNetworkOptions{ - netName: {}, - }, - } - err = networkInterface.allocIPs(&opts) - if i < 29 { - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName)) - Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1)) - // The (i+8)%29+2 part looks cryptic but it is actually simple, we already have 8 ips allocated above - // so we expect the 8 available ip. We have 29 assignable ip addresses in this subnet because "i"+8 can - // be greater than 30 we have to modulo by 29 to go back to the beginning. Also the first free ip is - // network address + 2, so we have to add 2 to the result - Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP(fmt.Sprintf("10.0.0.%d", (i+8)%29+2)).To4())) - } else { - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("IPAM error: failed to find free IP in range: 10.0.0.1 - 10.0.0.30")) - } - } - }) - - It("ipam with dhcp driver should not set ips", func() { - network, err := networkInterface.NetworkCreate(types.Network{ - IPAMOptions: map[string]string{ - "driver": types.DHCPIPAMDriver, - }, - }) - Expect(err).ToNot(HaveOccurred()) - - netName := network.Name - - opts := &types.NetworkOptions{ - ContainerID: "someContainerID", - Networks: map[string]types.PerNetworkOptions{ - netName: {}, - }, - } - - err = networkInterface.allocIPs(opts) - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName)) - Expect(opts.Networks[netName].StaticIPs).To(HaveLen(0)) - - err = networkInterface.getAssignedIPs(opts) - Expect(err).ToNot(HaveOccurred()) - Expect(opts.Networks).To(HaveKey(netName)) - Expect(opts.Networks[netName].StaticIPs).To(HaveLen(0)) - - // dealloc the ip - err = networkInterface.deallocIPs(opts) - Expect(err).ToNot(HaveOccurred()) - }) - -}) diff --git a/libpod/network/netavark/netavark_suite_test.go b/libpod/network/netavark/netavark_suite_test.go deleted file mode 100644 index 6063a54e3..000000000 --- a/libpod/network/netavark/netavark_suite_test.go +++ /dev/null @@ -1,75 +0,0 @@ -// +build linux - -package netavark_test - -import ( - "fmt" - "net" - "os" - "path/filepath" - "reflect" - "testing" - - "github.com/containers/podman/v3/libpod/network/netavark" - "github.com/containers/podman/v3/libpod/network/types" - "github.com/containers/podman/v3/libpod/network/util" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - gomegaTypes "github.com/onsi/gomega/types" -) - -func TestNetavark(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Netavark Suite") -} - -var netavarkBinary string - -func init() { - netavarkBinary = os.Getenv("NETAVARK_BINARY") - if netavarkBinary == "" { - netavarkBinary = "/usr/libexec/podman/netavark" - } -} - -func getNetworkInterface(confDir string, machine bool) (types.ContainerNetwork, error) { - return netavark.NewNetworkInterface(netavark.InitConfig{ - NetworkConfigDir: confDir, - IsMachine: machine, - NetavarkBinary: netavarkBinary, - IPAMDBPath: filepath.Join(confDir, "ipam.db"), - LockFile: filepath.Join(confDir, "netavark.lock"), - }) -} - -// EqualSubnet is a custom GomegaMatcher to match a subnet -// This makes sure to not use the 16 bytes ip representation. -func EqualSubnet(subnet *net.IPNet) gomegaTypes.GomegaMatcher { - return &equalSubnetMatcher{ - expected: subnet, - } -} - -type equalSubnetMatcher struct { - expected *net.IPNet -} - -func (m *equalSubnetMatcher) Match(actual interface{}) (bool, error) { - util.NormalizeIP(&m.expected.IP) - - subnet, ok := actual.(*net.IPNet) - if !ok { - return false, fmt.Errorf("EqualSubnet expects a *net.IPNet") - } - util.NormalizeIP(&subnet.IP) - - return reflect.DeepEqual(subnet, m.expected), nil -} - -func (m *equalSubnetMatcher) FailureMessage(actual interface{}) string { - return fmt.Sprintf("Expected subnet %#v to equal subnet %#v", actual, m.expected) -} - -func (m *equalSubnetMatcher) NegatedFailureMessage(actual interface{}) string { - return fmt.Sprintf("Expected subnet %#v not to equal subnet %#v", actual, m.expected) -} diff --git a/libpod/network/netavark/network.go b/libpod/network/netavark/network.go deleted file mode 100644 index 540d8d6e5..000000000 --- a/libpod/network/netavark/network.go +++ /dev/null @@ -1,314 +0,0 @@ -// +build linux - -package netavark - -import ( - "encoding/json" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" - - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/libpod/network/internal/util" - "github.com/containers/podman/v3/libpod/network/types" - pkgutil "github.com/containers/podman/v3/pkg/util" - "github.com/containers/storage/pkg/lockfile" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -type netavarkNetwork struct { - // networkConfigDir is directory where the network config files are stored. - networkConfigDir string - - // netavarkBinary is the path to the netavark binary. - netavarkBinary string - - // defaultNetwork is the name for the default network. - defaultNetwork string - // defaultSubnet is the default subnet for the default network. - defaultSubnet types.IPNet - - // ipamDBPath is the path to the ip allocation bolt db - ipamDBPath string - - // isMachine describes whenever podman runs in a podman machine environment. - isMachine bool - - // syslog describes whenever the netavark debbug output should be log to the syslog as well. - // This will use logrus to do so, make sure logrus is set up to log to the syslog. - syslog bool - - // lock is a internal lock for critical operations - lock lockfile.Locker - - // 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]*types.Network -} - -type InitConfig struct { - // NetworkConfigDir is directory where the network config files are stored. - NetworkConfigDir string - - // NetavarkBinary is the path to the netavark binary. - NetavarkBinary string - - // IPAMDBPath is the path to the ipam database. This should be on a tmpfs. - // If empty defaults to XDG_RUNTIME_DIR/netavark/ipam.db or /run/netavark/ipam.db as root. - IPAMDBPath string - - // DefaultNetwork is the name for the default network. - DefaultNetwork string - // DefaultSubnet is the default subnet for the default network. - DefaultSubnet string - - // IsMachine describes whenever podman runs in a podman machine environment. - IsMachine bool - - // LockFile is the path to lock file. - LockFile string - - // Syslog describes whenever the netavark debbug output should be log to the syslog as well. - // This will use logrus to do so, make sure logrus is set up to log to the syslog. - Syslog bool -} - -// NewNetworkInterface creates the ContainerNetwork interface for the netavark backend. -// Note: The networks are not loaded from disk until a method is called. -func NewNetworkInterface(conf InitConfig) (types.ContainerNetwork, error) { - // TODO: consider using a shared memory lock - lock, err := lockfile.GetLockfile(conf.LockFile) - if err != nil { - return nil, err - } - - defaultNetworkName := conf.DefaultNetwork - if defaultNetworkName == "" { - defaultNetworkName = types.DefaultNetworkName - } - - defaultSubnet := conf.DefaultSubnet - if defaultSubnet == "" { - defaultSubnet = types.DefaultSubnet - } - defaultNet, err := types.ParseCIDR(defaultSubnet) - if err != nil { - return nil, errors.Wrap(err, "failed to parse default subnet") - } - - ipamdbPath := conf.IPAMDBPath - if ipamdbPath == "" { - runDir, err := pkgutil.GetRuntimeDir() - if err != nil { - return nil, err - } - // as root runtimeDir is empty so use /run - if runDir == "" { - runDir = "/run" - } - ipamdbPath = filepath.Join(runDir, "netavark") - if err := os.MkdirAll(ipamdbPath, 0700); err != nil { - return nil, errors.Wrap(err, "failed to create ipam db path") - } - ipamdbPath = filepath.Join(ipamdbPath, "ipam.db") - } - - if err := os.MkdirAll(conf.NetworkConfigDir, 0755); err != nil { - return nil, err - } - - n := &netavarkNetwork{ - networkConfigDir: conf.NetworkConfigDir, - netavarkBinary: conf.NetavarkBinary, - ipamDBPath: ipamdbPath, - defaultNetwork: defaultNetworkName, - defaultSubnet: defaultNet, - isMachine: conf.IsMachine, - lock: lock, - syslog: conf.Syslog, - } - - return n, nil -} - -// Drivers will return the list of supported network drivers -// for this interface. -func (n *netavarkNetwork) Drivers() []string { - return []string{types.BridgeNetworkDriver} -} - -func (n *netavarkNetwork) loadNetworks() error { - // check the mod time of the config dir - f, err := os.Stat(n.networkConfigDir) - if err != nil { - return err - } - 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 - - files, err := ioutil.ReadDir(n.networkConfigDir) - if err != nil && !errors.Is(err, os.ErrNotExist) { - return err - } - - networks := make(map[string]*types.Network, len(files)) - for _, f := range files { - if f.IsDir() { - continue - } - if filepath.Ext(f.Name()) != ".json" { - continue - } - - path := filepath.Join(n.networkConfigDir, f.Name()) - file, err := os.Open(path) - if err != nil { - // do not log ENOENT errors - if !errors.Is(err, os.ErrNotExist) { - logrus.Warnf("Error loading network config file %q: %v", path, err) - } - continue - } - network := new(types.Network) - err = json.NewDecoder(file).Decode(network) - if err != nil { - logrus.Warnf("Error reading network config file %q: %v", path, err) - continue - } - - // check that the filename matches the network name - if network.Name+".json" != f.Name() { - logrus.Warnf("Network config name %q does not match file name %q, skipping", network.Name, f.Name()) - continue - } - - if !define.NameRegex.MatchString(network.Name) { - logrus.Warnf("Network config %q has invalid name: %q, skipping: %v", path, network.Name, define.RegexError) - continue - } - - err = parseNetwork(network) - if err != nil { - logrus.Warnf("Network config %q could not be parsed, skipping: %v", path, err) - continue - } - - logrus.Debugf("Successfully loaded network %s: %v", network.Name, network) - networks[network.Name] = network - } - - // 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 errors.Wrapf(err, "failed to create default network %s", n.defaultNetwork) - } - networks[n.defaultNetwork] = networkInfo - } - logrus.Debugf("Successfully loaded %d networks", len(networks)) - n.networks = networks - return nil -} - -func parseNetwork(network *types.Network) error { - if network.Labels == nil { - network.Labels = map[string]string{} - } - if network.Options == nil { - network.Options = map[string]string{} - } - if network.IPAMOptions == nil { - network.IPAMOptions = map[string]string{} - } - - if len(network.ID) != 64 { - return errors.Errorf("invalid network ID %q", network.ID) - } - - return util.ValidateSubnets(network, nil) -} - -func (n *netavarkNetwork) createDefaultNetwork() (*types.Network, error) { - net := types.Network{ - Name: n.defaultNetwork, - NetworkInterface: defaultBridgeName + "0", - // Important do not change this ID - ID: "2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9", - 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 *netavarkNetwork) getNetwork(nameOrID string) (*types.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 *types.Network - for _, val := range n.networks { - // This should not happen because we already looked up the map by name but check anyway. - if val.Name == nameOrID { - return val, nil - } - - if strings.HasPrefix(val.ID, nameOrID) { - if net != nil { - return nil, errors.Errorf("more than one result for network ID %s", nameOrID) - } - net = val - } - } - if net != nil { - return net, nil - } - return nil, errors.Wrapf(define.ErrNoSuchNetwork, "unable to find network with name or ID %s", nameOrID) -} - -// Implement the NetUtil interface for easy code sharing with other network interfaces. - -// ForEach call the given function for each network -func (n *netavarkNetwork) ForEach(run func(types.Network)) { - for _, val := range n.networks { - run(*val) - } -} - -// Len return the number of networks -func (n *netavarkNetwork) Len() int { - return len(n.networks) -} - -// DefaultInterfaceName return the default cni bridge name, must be suffixed with a number. -func (n *netavarkNetwork) DefaultInterfaceName() string { - return defaultBridgeName -} - -func (n *netavarkNetwork) Network(nameOrID string) (*types.Network, error) { - network, err := n.getNetwork(nameOrID) - if err != nil { - return nil, err - } - return network, nil -} diff --git a/libpod/network/netavark/run.go b/libpod/network/netavark/run.go deleted file mode 100644 index 0ac20daee..000000000 --- a/libpod/network/netavark/run.go +++ /dev/null @@ -1,127 +0,0 @@ -// +build linux - -package netavark - -import ( - "encoding/json" - "fmt" - - "github.com/containers/podman/v3/libpod/network/internal/util" - "github.com/containers/podman/v3/libpod/network/types" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -type netavarkOptions struct { - types.NetworkOptions - Networks map[string]*types.Network `json:"network_info"` -} - -// Setup will setup the container network namespace. It returns -// a map of StatusBlocks, the key is the network name. -func (n *netavarkNetwork) 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 - } - - // allocate IPs in the IPAM db - err = n.allocIPs(&options.NetworkOptions) - if err != nil { - return nil, err - } - - netavarkOpts, err := n.convertNetOpts(options.NetworkOptions) - if err != nil { - return nil, errors.Wrap(err, "failed to convert net opts") - } - - // trace output to get the json - if logrus.IsLevelEnabled(logrus.TraceLevel) { - b, err := json.Marshal(&netavarkOpts) - if err != nil { - return nil, err - } - // show the full netavark command so we can easily reproduce errors from the cli - logrus.Tracef("netavark command: printf '%s' | %s setup %s", string(b), n.netavarkBinary, namespacePath) - } - - result := map[string]types.StatusBlock{} - err = n.execNetavark([]string{"setup", namespacePath}, netavarkOpts, &result) - if err != nil { - // lets dealloc ips to prevent leaking - if err := n.deallocIPs(&options.NetworkOptions); err != nil { - logrus.Error(err) - } - return nil, err - } - - // make sure that the result makes sense - if len(result) != len(options.Networks) { - logrus.Errorf("unexpected netavark result: %v", result) - return nil, fmt.Errorf("unexpected netavark result length, want (%d), got (%d) networks", len(options.Networks), len(result)) - } - - return result, err -} - -// Teardown will teardown the container network namespace. -func (n *netavarkNetwork) Teardown(namespacePath string, options types.TeardownOptions) error { - n.lock.Lock() - defer n.lock.Unlock() - err := n.loadNetworks() - if err != nil { - return err - } - - // get IPs from the IPAM db - err = n.getAssignedIPs(&options.NetworkOptions) - if err != nil { - // when there is an error getting the ips we should still continue - // to call teardown for netavark to prevent leaking network interfaces - logrus.Error(err) - } - - netavarkOpts, err := n.convertNetOpts(options.NetworkOptions) - if err != nil { - return errors.Wrap(err, "failed to convert net opts") - } - - retErr := n.execNetavark([]string{"teardown", namespacePath}, netavarkOpts, nil) - - // when netavark returned an error we still free the used ips - // otherwise we could end up in a state where block the ips forever - err = n.deallocIPs(&netavarkOpts.NetworkOptions) - if err != nil { - if retErr != nil { - logrus.Error(err) - } else { - retErr = err - } - } - - return retErr -} - -func (n *netavarkNetwork) convertNetOpts(opts types.NetworkOptions) (*netavarkOptions, error) { - netavarkOptions := netavarkOptions{ - NetworkOptions: opts, - Networks: make(map[string]*types.Network, len(opts.Networks)), - } - - for network := range opts.Networks { - net, err := n.getNetwork(network) - if err != nil { - return nil, err - } - netavarkOptions.Networks[network] = net - } - return &netavarkOptions, nil -} diff --git a/libpod/network/netavark/run_test.go b/libpod/network/netavark/run_test.go deleted file mode 100644 index f79e6d812..000000000 --- a/libpod/network/netavark/run_test.go +++ /dev/null @@ -1,699 +0,0 @@ -// +build linux - -package netavark_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 ( - "io/ioutil" - "net" - "os" - "strconv" - "sync" - "time" - - "github.com/containernetworking/plugins/pkg/ns" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" - - "github.com/containers/podman/v3/libpod/network/types" - "github.com/containers/podman/v3/libpod/network/util" - "github.com/containers/podman/v3/pkg/netns" - "github.com/containers/podman/v3/pkg/rootless" - "github.com/containers/storage/pkg/stringid" -) - -var _ = Describe("run netavark", func() { - var ( - libpodNet types.ContainerNetwork - confDir string - netNSTest ns.NetNS - netNSContainer ns.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 mountns to mount a tmpfs to /var/lib/cni. - runTest := func(run func()) { - netNSTest.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - // we have to setup the loopback adapter in this netns to use port forwarding - link, err := netlink.LinkByName("lo") - Expect(err).To(BeNil(), "Failed to get loopback adapter") - err = netlink.LinkSetUp(link) - Expect(err).To(BeNil(), "Failed to set loopback adapter up") - run() - return nil - }) - } - - BeforeEach(func() { - if _, ok := os.LookupEnv("NETAVARK_BINARY"); !ok { - Skip("NETAVARK_BINARY not set skip run tests") - } - - // set the logrus settings - logrus.SetLevel(logrus.TraceLevel) - // disable extra quotes so we can easily copy the netavark command - logrus.SetFormatter(&logrus.TextFormatter{DisableQuote: true}) - logrus.SetOutput(os.Stderr) - // 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 rootless.IsRootless() { - Skip("this test needs to be run as root") - } - - var err error - confDir, err = ioutil.TempDir("", "podman_netavark_test") - if err != nil { - Fail("Failed to create tmpdir") - } - - netNSTest, err = netns.NewNS() - if err != nil { - Fail("Failed to create netns") - } - - netNSContainer, err = netns.NewNS() - if err != nil { - Fail("Failed to create netns") - } - - // Force iptables driver, firewalld is broken inside the extra - // namespace because it still connects to firewalld on the host. - _ = os.Setenv("NETAVARK_FW", "iptables") - }) - - JustBeforeEach(func() { - var err error - libpodNet, err = getNetworkInterface(confDir, false) - if err != nil { - Fail("Failed to create NewCNINetworkInterface") - } - }) - - AfterEach(func() { - logrus.SetFormatter(&logrus.TextFormatter{}) - logrus.SetLevel(logrus.InfoLevel) - os.RemoveAll(confDir) - - netns.UnmountNS(netNSTest) - netNSTest.Close() - - netns.UnmountNS(netNSContainer) - netNSContainer.Close() - - _ = os.Unsetenv("NETAVARK_FW") - }) - - It("test basic setup", func() { - runTest(func() { - defNet := types.DefaultNetworkName - intName := "eth0" - opts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: "someID", - ContainerName: "someName", - Networks: map[string]types.PerNetworkOptions{ - defNet: { - InterfaceName: intName, - }, - }, - }, - } - res, err := libpodNet.Setup(netNSContainer.Path(), opts) - 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)) - ip := res[defNet].Interfaces[intName].Subnets[0].IPNet.IP - Expect(ip.String()).To(ContainSubstring("10.88.0.")) - gw := res[defNet].Interfaces[intName].Subnets[0].Gateway - util.NormalizeIP(&gw) - Expect(gw.String()).To(Equal("10.88.0.1")) - macAddress := res[defNet].Interfaces[intName].MacAddress - Expect(macAddress).To(HaveLen(6)) - // default network has no dns - Expect(res[defNet].DNSServerIPs).To(BeEmpty()) - Expect(res[defNet].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(intName) - Expect(err).To(BeNil()) - Expect(i.Name).To(Equal(intName)) - Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macAddress))) - addrs, err := i.Addrs() - Expect(err).To(BeNil()) - subnet := &net.IPNet{ - IP: ip, - Mask: net.CIDRMask(16, 32), - } - Expect(addrs).To(ContainElements(EqualSubnet(subnet))) - - // check loopback adapter - i, err = net.InterfaceByName("lo") - Expect(err).To(BeNil()) - 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).To(BeNil()) - - // default bridge name - bridgeName := "podman0" - // check settings on the host side - i, err := net.InterfaceByName(bridgeName) - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal(bridgeName)) - addrs, err := i.Addrs() - Expect(err).ToNot(HaveOccurred()) - // test that the gateway ip is assigned to the interface - subnet := &net.IPNet{ - IP: gw, - Mask: net.CIDRMask(16, 32), - } - Expect(addrs).To(ContainElements(EqualSubnet(subnet))) - - wg := &sync.WaitGroup{} - expected := stringid.GenerateNonCryptoID() - // now check ip connectivity - err = netNSContainer.Do(func(_ ns.NetNS) error { - wg.Add(1) - runNetListener(wg, "tcp", "0.0.0.0", 5000, expected) - return nil - }) - Expect(err).ToNot(HaveOccurred()) - - conn, err := net.Dial("tcp", ip.String()+":5000") - Expect(err).To(BeNil()) - _, err = conn.Write([]byte(expected)) - Expect(err).To(BeNil()) - conn.Close() - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(opts)) - Expect(err).ToNot(HaveOccurred()) - wg.Wait() - }) - }) - - It("setup two containers", func() { - runTest(func() { - defNet := types.DefaultNetworkName - intName := "eth0" - setupOpts1 := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - defNet: {InterfaceName: intName}, - }, - }, - } - res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts1) - 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)) - ip1 := res[defNet].Interfaces[intName].Subnets[0].IPNet.IP - Expect(ip1.String()).To(ContainSubstring("10.88.0.")) - Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) - - setupOpts2 := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - defNet: {InterfaceName: intName}, - }, - }, - } - - netNSContainer2, err := netns.NewNS() - Expect(err).ToNot(HaveOccurred()) - defer netns.UnmountNS(netNSContainer2) - defer netNSContainer2.Close() - - res, err = libpodNet.Setup(netNSContainer2.Path(), setupOpts2) - 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)) - ip2 := res[defNet].Interfaces[intName].Subnets[0].IPNet.IP - Expect(ip2.String()).To(ContainSubstring("10.88.0.")) - Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) - Expect(ip1.Equal(ip2)).To(BeFalse(), "IP1 %s should not be equal to IP2 %s", ip1.String(), ip2.String()) - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts1)) - Expect(err).ToNot(HaveOccurred()) - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts2)) - Expect(err).ToNot(HaveOccurred()) - }) - }) - - It("setup dualstack network", func() { - runTest(func() { - s1, _ := types.ParseCIDR("10.0.0.1/24") - s2, _ := types.ParseCIDR("fd10:88:a::/64") - network, err := libpodNet.NetworkCreate(types.Network{ - Subnets: []types.Subnet{ - {Subnet: s1}, {Subnet: s2}, - }, - }) - Expect(err).ToNot(HaveOccurred()) - - netName := network.Name - intName := "eth0" - - setupOpts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: stringid.GenerateNonCryptoID(), - Networks: map[string]types.PerNetworkOptions{ - netName: {InterfaceName: intName}, - }, - }, - } - 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(2)) - ip1 := res[netName].Interfaces[intName].Subnets[0].IPNet.IP - Expect(ip1.String()).To(ContainSubstring("10.0.0.")) - gw1 := res[netName].Interfaces[intName].Subnets[0].Gateway - Expect(gw1.String()).To(Equal("10.0.0.1")) - ip2 := res[netName].Interfaces[intName].Subnets[1].IPNet.IP - Expect(ip2.String()).To(ContainSubstring("fd10:88:a::")) - gw2 := res[netName].Interfaces[intName].Subnets[0].Gateway - Expect(gw2.String()).To(Equal("fd10:88:a::1")) - 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 { - 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()) - subnet1 := s1.IPNet - subnet1.IP = ip1 - subnet2 := s2.IPNet - subnet2.IP = ip2 - Expect(addrs).To(ContainElements(EqualSubnet(&subnet1), EqualSubnet(&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()) - - bridgeName := network.NetworkInterface - // check settings on the host side - i, err := net.InterfaceByName(bridgeName) - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal(bridgeName)) - addrs, err := i.Addrs() - Expect(err).ToNot(HaveOccurred()) - // test that the gateway ip is assigned to the interface - subnet1 := s1.IPNet - subnet1.IP = gw1 - subnet2 := s2.IPNet - subnet2.IP = gw2 - Expect(addrs).To(ContainElements(EqualSubnet(&subnet1), EqualSubnet(&subnet2))) - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).ToNot(HaveOccurred()) - }) - }) - - It("setup two networks", func() { - runTest(func() { - s1, _ := types.ParseCIDR("10.0.0.1/24") - network1, err := libpodNet.NetworkCreate(types.Network{ - Subnets: []types.Subnet{ - {Subnet: s1}, - }, - }) - Expect(err).ToNot(HaveOccurred()) - - netName1 := network1.Name - intName1 := "eth0" - - s2, _ := types.ParseCIDR("10.1.0.0/24") - network2, err := libpodNet.NetworkCreate(types.Network{ - Subnets: []types.Subnet{ - {Subnet: s2}, - }, - }) - Expect(err).ToNot(HaveOccurred()) - - netName2 := network2.Name - intName2 := "eth1" - - 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).To(HaveKey(netName2)) - Expect(res[netName1].Interfaces).To(HaveKey(intName1)) - Expect(res[netName2].Interfaces).To(HaveKey(intName2)) - Expect(res[netName1].Interfaces[intName1].Subnets).To(HaveLen(1)) - ip1 := res[netName1].Interfaces[intName1].Subnets[0].IPNet.IP - Expect(ip1.String()).To(ContainSubstring("10.0.0.")) - gw1 := res[netName1].Interfaces[intName1].Subnets[0].Gateway - Expect(gw1.String()).To(Equal("10.0.0.1")) - ip2 := res[netName2].Interfaces[intName2].Subnets[0].IPNet.IP - Expect(ip2.String()).To(ContainSubstring("10.1.0.")) - gw2 := res[netName2].Interfaces[intName2].Subnets[0].Gateway - Expect(gw2.String()).To(Equal("10.1.0.1")) - mac1 := res[netName1].Interfaces[intName1].MacAddress - Expect(mac1).To(HaveLen(6)) - mac2 := res[netName2].Interfaces[intName2].MacAddress - Expect(mac2).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)) - addrs, err := i.Addrs() - Expect(err).ToNot(HaveOccurred()) - subnet1 := s1.IPNet - subnet1.IP = ip1 - Expect(addrs).To(ContainElements(EqualSubnet(&subnet1))) - - i, err = net.InterfaceByName(intName2) - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal(intName2)) - addrs, err = i.Addrs() - Expect(err).ToNot(HaveOccurred()) - subnet2 := s2.IPNet - subnet2.IP = ip2 - Expect(addrs).To(ContainElements(EqualSubnet(&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()) - - bridgeName1 := network1.NetworkInterface - // check settings on the host side - i, err := net.InterfaceByName(bridgeName1) - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal(bridgeName1)) - addrs, err := i.Addrs() - Expect(err).ToNot(HaveOccurred()) - // test that the gateway ip is assigned to the interface - subnet1 := s1.IPNet - subnet1.IP = gw1 - Expect(addrs).To(ContainElements(EqualSubnet(&subnet1))) - - bridgeName2 := network2.NetworkInterface - // check settings on the host side - i, err = net.InterfaceByName(bridgeName2) - Expect(err).ToNot(HaveOccurred()) - Expect(i.Name).To(Equal(bridgeName2)) - addrs, err = i.Addrs() - Expect(err).ToNot(HaveOccurred()) - // test that the gateway ip is assigned to the interface - subnet2 := s2.IPNet - subnet2.IP = gw2 - Expect(addrs).To(ContainElements(EqualSubnet(&subnet2))) - - 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).To(BeNil()) - Expect(res).To(HaveLen(1)) - Expect(res).To(HaveKey(defNet)) - Expect(res[defNet].Interfaces).To(HaveKey(intName)) - Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1)) - Expect(res[defNet].Interfaces[intName].Subnets[0].IPNet.IP.String()).To(ContainSubstring("10.88.0.")) - Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) - // default network has no dns - Expect(res[defNet].DNSServerIPs).To(BeEmpty()) - Expect(res[defNet].DNSSearchDomains).To(BeEmpty()) - var wg sync.WaitGroup - wg.Add(1) - // start a listener in the container ns - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - runNetListener(&wg, protocol, "0.0.0.0", 5000, testdata) - return nil - }) - Expect(err).To(BeNil()) - - conn, err := net.Dial(protocol, "127.0.0.1:5000") - Expect(err).To(BeNil()) - _, err = conn.Write([]byte(testdata)) - Expect(err).To(BeNil()) - conn.Close() - - // wait for the listener to finish - wg.Wait() - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).To(BeNil()) - }) - }) - - 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).To(BeNil()) - Expect(res).To(HaveLen(1)) - Expect(res).To(HaveKey(defNet)) - Expect(res[defNet].Interfaces).To(HaveKey(intName)) - Expect(res[defNet].Interfaces[intName].Subnets).To(HaveLen(1)) - containerIP := res[defNet].Interfaces[intName].Subnets[0].IPNet.IP.String() - Expect(containerIP).To(ContainSubstring("10.88.0.")) - Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) - // default network has no dns - Expect(res[defNet].DNSServerIPs).To(BeEmpty()) - Expect(res[defNet].DNSSearchDomains).To(BeEmpty()) - - // loop over all ports - for p := 5001; p < 5004; p++ { - port := p - var wg sync.WaitGroup - wg.Add(1) - testdata := stringid.GenerateNonCryptoID() - // start a listener in the container ns - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - runNetListener(&wg, protocol, containerIP, port-1, testdata) - return nil - }) - Expect(err).To(BeNil()) - - conn, err := net.Dial(protocol, net.JoinHostPort("127.0.0.1", strconv.Itoa(port))) - Expect(err).To(BeNil()) - _, err = conn.Write([]byte(testdata)) - Expect(err).To(BeNil()) - conn.Close() - - // wait for the listener to finish - wg.Wait() - } - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) - Expect(err).To(BeNil()) - }) - }) - } - - It("simple teardown", func() { - runTest(func() { - defNet := types.DefaultNetworkName - intName := "eth0" - opts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: "someID", - ContainerName: "someName", - Networks: map[string]types.PerNetworkOptions{ - defNet: { - InterfaceName: intName, - }, - }, - }, - } - res, err := libpodNet.Setup(netNSContainer.Path(), opts) - 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)) - ip := res[defNet].Interfaces[intName].Subnets[0].IPNet.IP - Expect(ip.String()).To(ContainSubstring("10.88.0.")) - gw := res[defNet].Interfaces[intName].Subnets[0].Gateway - Expect(gw.String()).To(Equal("10.88.0.1")) - macAddress := res[defNet].Interfaces[intName].MacAddress - Expect(macAddress).To(HaveLen(6)) - - err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(opts)) - Expect(err).ToNot(HaveOccurred()) - err = netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - // check that the container interface is removed - _, err := net.InterfaceByName(intName) - Expect(err).To(HaveOccurred()) - return nil - }) - Expect(err).ToNot(HaveOccurred()) - - // default bridge name - bridgeName := "podman0" - // check that bridge interface was removed - _, err = net.InterfaceByName(bridgeName) - Expect(err).To(HaveOccurred()) - }) - }) - - It("test netavark error", func() { - runTest(func() { - intName := "eth0" - err := netNSContainer.Do(func(_ ns.NetNS) error { - defer GinkgoRecover() - - attr := netlink.NewLinkAttrs() - attr.Name = "eth0" - err := netlink.LinkAdd(&netlink.Bridge{LinkAttrs: attr}) - Expect(err).ToNot(HaveOccurred()) - return nil - }) - Expect(err).ToNot(HaveOccurred()) - defNet := types.DefaultNetworkName - opts := types.SetupOptions{ - NetworkOptions: types.NetworkOptions{ - ContainerID: "someID", - ContainerName: "someName", - Networks: map[string]types.PerNetworkOptions{ - defNet: { - InterfaceName: intName, - }, - }, - }, - } - _, err = libpodNet.Setup(netNSContainer.Path(), opts) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("interface eth0 already exists on container namespace")) - }) - }) -}) - -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).To(BeNil()) - // make sure to read in a separate goroutine to not block - go func() { - defer GinkgoRecover() - defer wg.Done() - defer ln.Close() - conn, err := ln.Accept() - Expect(err).To(BeNil()) - defer conn.Close() - conn.SetDeadline(time.Now().Add(1 * time.Second)) - data, err := ioutil.ReadAll(conn) - Expect(err).To(BeNil()) - Expect(string(data)).To(Equal(expectedData)) - }() - case "udp": - conn, err := net.ListenUDP("udp", &net.UDPAddr{ - IP: net.ParseIP(ip), - Port: port, - }) - Expect(err).To(BeNil()) - conn.SetDeadline(time.Now().Add(1 * time.Second)) - go func() { - defer GinkgoRecover() - defer wg.Done() - defer conn.Close() - data := make([]byte, len(expectedData)) - i, err := conn.Read(data) - Expect(err).To(BeNil()) - Expect(i).To(Equal(len(expectedData))) - Expect(string(data)).To(Equal(expectedData)) - }() - default: - Fail("unsupported protocol") - } -} diff --git a/libpod/network/netavark/testfiles/invalid/broken.json b/libpod/network/netavark/testfiles/invalid/broken.json deleted file mode 100644 index 8968ddc73..000000000 --- a/libpod/network/netavark/testfiles/invalid/broken.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "bridge", - "id": "17f29b073143d8cd97b5bbe492bdeffec1c5fee55cc1fe2112c8b9335f8b6121", - "driver": "bridge", - "network_interface": "podman9", - "created": "2021-10-06T18:50:54.25770461+02:00", - "subnets": [ - { - "subnet": "10.89.8.0/24", - "gateway": "10.89.8.1", - "lease_range": { - "start_ip": "10.89.8.20", - "end_ip": "10.89.8.50" - } - } - ], diff --git a/libpod/network/netavark/testfiles/invalid/invalid name.json b/libpod/network/netavark/testfiles/invalid/invalid name.json deleted file mode 100644 index 02b441279..000000000 --- a/libpod/network/netavark/testfiles/invalid/invalid name.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "invalid name", - "id": "6839f44f0fd01c5c5830856b66a1d7ce46842dd8798be0addf96f7255ce9f889", - "driver": "bridge", - "network_interface": "podman9", - "created": "2021-10-06T18:50:54.25770461+02:00", - "subnets": [ - { - "subnet": "10.89.8.0/24", - "gateway": "10.89.8.1" - } - ], - "ipv6_enabled": false, - "internal": false, - "dns_enabled": true, - "ipam_options": { - "driver": "host-local" - } -} diff --git a/libpod/network/netavark/testfiles/invalid/invalid_gateway.json b/libpod/network/netavark/testfiles/invalid/invalid_gateway.json deleted file mode 100644 index 6e3a83156..000000000 --- a/libpod/network/netavark/testfiles/invalid/invalid_gateway.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "invalid_gateway", - "id": "49be6e401e7f8b9844afb969dcbc96e78205ed86ec1e5a46150bd4ab4fdd5686", - "driver": "bridge", - "network_interface": "podman9", - "created": "2021-10-06T18:50:54.25770461+02:00", - "subnets": [ - { - "subnet": "10.89.9.0/24", - "gateway": "10.89.100.1" - } - ], - "ipv6_enabled": false, - "internal": false, - "dns_enabled": true, - "ipam_options": { - "driver": "host-local" - } -} diff --git a/libpod/network/netavark/testfiles/invalid/name_missmatch.json b/libpod/network/netavark/testfiles/invalid/name_missmatch.json deleted file mode 100644 index a3142d8bb..000000000 --- a/libpod/network/netavark/testfiles/invalid/name_missmatch.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "name_miss", - "id": "3bed2cb3a3acf7b6a8ef408420cc682d5520e26976d354254f528c965612054f", - "driver": "bridge", - "network_interface": "podman8", - "created": "2021-10-06T18:50:54.25770461+02:00", - "subnets": [ - { - "subnet": "10.89.7.0/24", - "gateway": "10.89.7.1" - } - ], - "ipv6_enabled": false, - "internal": true, - "dns_enabled": false, - "ipam_options": { - "driver": "host-local" - } -} diff --git a/libpod/network/netavark/testfiles/invalid/wrongID.json b/libpod/network/netavark/testfiles/invalid/wrongID.json deleted file mode 100644 index 7c1446306..000000000 --- a/libpod/network/netavark/testfiles/invalid/wrongID.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "wrongID", - "id": "someID", - "driver": "bridge", - "network_interface": "podman1", - "created": "2021-10-06T18:50:54.25770461+02:00", - "subnets": [ - { - "subnet": "10.89.0.0/24", - "gateway": "10.89.0.1" - } - ], - "ipv6_enabled": false, - "internal": false, - "dns_enabled": false, - "ipam_options": { - "driver": "host-local" - } -} diff --git a/libpod/network/netavark/testfiles/valid/bridge.json b/libpod/network/netavark/testfiles/valid/bridge.json deleted file mode 100644 index f4ec82188..000000000 --- a/libpod/network/netavark/testfiles/valid/bridge.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "bridge", - "id": "17f29b073143d8cd97b5bbe492bdeffec1c5fee55cc1fe2112c8b9335f8b6121", - "driver": "bridge", - "network_interface": "podman9", - "created": "2021-10-06T18:50:54.25770461+02:00", - "subnets": [ - { - "subnet": "10.89.8.0/24", - "gateway": "10.89.8.1", - "lease_range": { - "start_ip": "10.89.8.20", - "end_ip": "10.89.8.50" - } - } - ], - "ipv6_enabled": false, - "internal": false, - "dns_enabled": true, - "ipam_options": { - "driver": "host-local" - } -} diff --git a/libpod/network/netavark/testfiles/valid/dualstack.json b/libpod/network/netavark/testfiles/valid/dualstack.json deleted file mode 100644 index bb4168f3a..000000000 --- a/libpod/network/netavark/testfiles/valid/dualstack.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "dualstack", - "id": "6839f44f0fd01c5c5830856b66a1d7ce46842dd8798be0addf96f7255ce9f889", - "driver": "bridge", - "network_interface": "podman21", - "created": "2021-10-06T18:50:54.25770461+02:00", - "subnets": [ - { - "subnet": "fd10:88:a::/64", - "gateway": "fd10:88:a::1" - }, - { - "subnet": "10.89.19.0/24", - "gateway": "10.89.19.10" - } - ], - "ipv6_enabled": true, - "internal": false, - "dns_enabled": true, - "ipam_options": { - "driver": "host-local" - } -} diff --git a/libpod/network/netavark/testfiles/valid/internal.json b/libpod/network/netavark/testfiles/valid/internal.json deleted file mode 100644 index 3ccdd3889..000000000 --- a/libpod/network/netavark/testfiles/valid/internal.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "internal", - "id": "3bed2cb3a3acf7b6a8ef408420cc682d5520e26976d354254f528c965612054f", - "driver": "bridge", - "network_interface": "podman8", - "created": "2021-10-06T18:50:54.25770461+02:00", - "subnets": [ - { - "subnet": "10.89.7.0/24" - } - ], - "ipv6_enabled": false, - "internal": true, - "dns_enabled": false, - "ipam_options": { - "driver": "host-local" - } -} diff --git a/libpod/network/netavark/testfiles/valid/label.json b/libpod/network/netavark/testfiles/valid/label.json deleted file mode 100644 index c4ed637ec..000000000 --- a/libpod/network/netavark/testfiles/valid/label.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "label", - "id": "1aca80e8b55c802f7b43740da2990e1b5735bbb323d93eb5ebda8395b04025e2", - "driver": "bridge", - "network_interface": "podman15", - "created": "2021-10-06T18:50:54.25770461+02:00", - "subnets": [ - { - "subnet": "10.89.13.0/24", - "gateway": "10.89.13.1" - } - ], - "ipv6_enabled": false, - "internal": false, - "dns_enabled": true, - "labels": { - "mykey": "value" - }, - "ipam_options": { - "driver": "host-local" - } -} diff --git a/libpod/network/netavark/testfiles/valid/mtu.json b/libpod/network/netavark/testfiles/valid/mtu.json deleted file mode 100644 index 53fa4c9bc..000000000 --- a/libpod/network/netavark/testfiles/valid/mtu.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "mtu", - "id": "49be6e401e7f8b9844afb969dcbc96e78205ed86ec1e5a46150bd4ab4fdd5686", - "driver": "bridge", - "network_interface": "podman13", - "created": "2021-10-06T18:50:54.25770461+02:00", - "subnets": [ - { - "subnet": "10.89.11.0/24", - "gateway": "10.89.11.1" - } - ], - "ipv6_enabled": false, - "internal": false, - "dns_enabled": true, - "options": { - "mtu": "1500" - }, - "ipam_options": { - "driver": "host-local" - } -} diff --git a/libpod/network/netavark/testfiles/valid/podman.json b/libpod/network/netavark/testfiles/valid/podman.json deleted file mode 100644 index 19acddc83..000000000 --- a/libpod/network/netavark/testfiles/valid/podman.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "podman", - "id": "2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9", - "driver": "bridge", - "network_interface": "podman0", - "created": "2021-10-06T18:50:54.25770461+02:00", - "subnets": [ - { - "subnet": "10.88.0.0/16", - "gateway": "10.88.0.1" - } - ], - "ipv6_enabled": false, - "internal": false, - "dns_enabled": false, - "ipam_options": { - "driver": "host-local" - } -} diff --git a/libpod/network/netavark/testfiles/valid/vlan.json b/libpod/network/netavark/testfiles/valid/vlan.json deleted file mode 100644 index 30c88ec49..000000000 --- a/libpod/network/netavark/testfiles/valid/vlan.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "vlan", - "id": "c3b258168c41c0bce97616716bef315eeed33eb1142904bfe7f32eb392c7cf80", - "driver": "bridge", - "network_interface": "podman14", - "created": "2021-10-06T18:50:54.25770461+02:00", - "subnets": [ - { - "subnet": "10.89.12.0/24", - "gateway": "10.89.12.1" - } - ], - "ipv6_enabled": false, - "internal": false, - "dns_enabled": true, - "options": { - "vlan": "5" - }, - "ipam_options": { - "driver": "host-local" - } -} diff --git a/libpod/network/types/const.go b/libpod/network/types/const.go deleted file mode 100644 index 916c6e6bf..000000000 --- a/libpod/network/types/const.go +++ /dev/null @@ -1,23 +0,0 @@ -package types - -const ( - // BridgeNetworkDriver defines the bridge driver - BridgeNetworkDriver = "bridge" - // DefaultNetworkDriver is the default network type used - DefaultNetworkDriver = BridgeNetworkDriver - // MacVLANNetworkDriver defines the macvlan driver - MacVLANNetworkDriver = "macvlan" - // MacVLANNetworkDriver defines the macvlan driver - IPVLANNetworkDriver = "ipvlan" - - // IPAM drivers - // HostLocalIPAMDriver store the ip - HostLocalIPAMDriver = "host-local" - // DHCPIPAMDriver get subnet and ip from dhcp server - DHCPIPAMDriver = "dhcp" - - // DefaultSubnet is the name that will be used for the default CNI network. - DefaultNetworkName = "podman" - // DefaultSubnet is the subnet that will be used for the default CNI network. - DefaultSubnet = "10.88.0.0/16" -) diff --git a/libpod/network/types/network.go b/libpod/network/types/network.go deleted file mode 100644 index 37fa11461..000000000 --- a/libpod/network/types/network.go +++ /dev/null @@ -1,278 +0,0 @@ -package types - -import ( - "encoding/json" - "net" - "time" -) - -type ContainerNetwork interface { - // NetworkCreate will take a partial filled Network and fill the - // missing fields. It creates the Network and returns the full Network. - NetworkCreate(Network) (Network, error) - // NetworkRemove will remove the Network with the given name or ID. - NetworkRemove(nameOrID string) error - // 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. - NetworkList(...FilterFunc) ([]Network, error) - // NetworkInspect will return the Network with the given name or ID. - NetworkInspect(nameOrID string) (Network, error) - - // Setup will setup the container network namespace. It returns - // a map of StatusBlocks, the key is the network name. - Setup(namespacePath string, options SetupOptions) (map[string]StatusBlock, error) - // Teardown will teardown the container network namespace. - Teardown(namespacePath string, options TeardownOptions) error - - // Drivers will return the list of supported network drivers - // for this interface. - Drivers() []string -} - -// Network describes the Network attributes. -type Network struct { - // Name of the Network. - Name string `json:"name"` - // ID of the Network. - ID string `json:"id"` - // Driver for this Network, e.g. bridge, macvlan... - Driver string `json:"driver"` - // NetworkInterface is the network interface name on the host. - NetworkInterface string `json:"network_interface,omitempty"` - // Created contains the timestamp when this network was created. - Created time.Time `json:"created,omitempty"` - // Subnets to use for this network. - Subnets []Subnet `json:"subnets,omitempty"` - // IPv6Enabled if set to true an ipv6 subnet should be created for this net. - IPv6Enabled bool `json:"ipv6_enabled"` - // Internal is whether the Network should not have external routes - // to public or other Networks. - Internal bool `json:"internal"` - // DNSEnabled is whether name resolution is active for container on - // this Network. - DNSEnabled bool `json:"dns_enabled"` - // Labels is a set of key-value labels that have been applied to the - // Network. - Labels map[string]string `json:"labels,omitempty"` - // Options is a set of key-value options that have been applied to - // the Network. - Options map[string]string `json:"options,omitempty"` - // IPAMOptions contains options used for the ip assignment. - IPAMOptions map[string]string `json:"ipam_options,omitempty"` -} - -// IPNet is used as custom net.IPNet type to add Marshal/Unmarshal methods. -type IPNet struct { - net.IPNet -} - -// ParseCIDR parse a string to IPNet -func ParseCIDR(cidr string) (IPNet, error) { - ip, net, err := net.ParseCIDR(cidr) - if err != nil { - return IPNet{}, err - } - // convert to 4 bytes if ipv4 - ipv4 := ip.To4() - if ipv4 != nil { - ip = ipv4 - } - net.IP = ip - return IPNet{*net}, err -} - -func (n *IPNet) MarshalText() ([]byte, error) { - return []byte(n.String()), nil -} - -func (n *IPNet) UnmarshalText(text []byte) error { - net, err := ParseCIDR(string(text)) - if err != nil { - return err - } - *n = net - return nil -} - -// HardwareAddr is the same as net.HardwareAddr except -// that it adds the json marshal/unmarshal methods. -// This allows us to read the mac from a json string -// and a byte array. -// swagger:model MacAddress -type HardwareAddr net.HardwareAddr - -func (h *HardwareAddr) String() string { - return (*net.HardwareAddr)(h).String() -} - -func (h *HardwareAddr) MarshalText() ([]byte, error) { - return []byte((*net.HardwareAddr)(h).String()), nil -} - -func (h *HardwareAddr) UnmarshalJSON(text []byte) error { - if len(text) == 0 { - *h = nil - return nil - } - - // if the json string start with a quote we got a string - // unmarshal the string and parse the mac from this string - if string(text[0]) == `"` { - var macString string - err := json.Unmarshal(text, &macString) - if err == nil { - mac, err := net.ParseMAC(macString) - if err == nil { - *h = HardwareAddr(mac) - return nil - } - } - } - // not a string or got an error fallback to the normal parsing - mac := make(net.HardwareAddr, 0, 6) - // use the standard json unmarshal for backwards compat - err := json.Unmarshal(text, &mac) - if err != nil { - return err - } - *h = HardwareAddr(mac) - return nil -} - -type Subnet struct { - // Subnet for this Network in CIDR form. - // swagger:strfmt string - Subnet IPNet `json:"subnet"` - // Gateway IP for this Network. - // swagger:strfmt string - Gateway net.IP `json:"gateway,omitempty"` - // LeaseRange contains the range where IP are leased. Optional. - LeaseRange *LeaseRange `json:"lease_range,omitempty"` -} - -// LeaseRange contains the range where IP are leased. -type LeaseRange struct { - // StartIP first IP in the subnet which should be used to assign ips. - // swagger:strfmt string - StartIP net.IP `json:"start_ip,omitempty"` - // EndIP last IP in the subnet which should be used to assign ips. - // swagger:strfmt string - EndIP net.IP `json:"end_ip,omitempty"` -} - -// StatusBlock contains the network information about a container -// connected to one Network. -type StatusBlock struct { - // Interfaces contains the created network interface in the container. - // The map key is the interface name. - Interfaces map[string]NetInterface `json:"interfaces,omitempty"` - // DNSServerIPs nameserver addresses which should be added to - // the containers resolv.conf file. - DNSServerIPs []net.IP `json:"dns_server_ips,omitempty"` - // DNSSearchDomains search domains which should be added to - // the containers resolv.conf file. - DNSSearchDomains []string `json:"dns_search_domains,omitempty"` -} - -// NetInterface contains the settings for a given network interface. -type NetInterface struct { - // Subnets list of assigned subnets with their gateway. - Subnets []NetAddress `json:"subnets,omitempty"` - // MacAddress for this Interface. - MacAddress HardwareAddr `json:"mac_address"` -} - -// NetAddress contains the ip address, subnet and gateway. -type NetAddress struct { - // IPNet of this NetAddress. Note that this is a subnet but it has to contain the - // actual ip of the network interface and not the network address. - IPNet IPNet `json:"ipnet"` - // Gateway for the network. This can be empty if there is no gateway, e.g. internal network. - Gateway net.IP `json:"gateway,omitempty"` -} - -// PerNetworkOptions are options which should be set on a per network basis. -type PerNetworkOptions struct { - // StaticIPs for this container. Optional. - StaticIPs []net.IP `json:"static_ips,omitempty"` - // Aliases contains a list of names which the dns server should resolve - // to this container. Should only be set when DNSEnabled is true on the Network. - // If aliases are set but there is no dns support for this network the - // network interface implementation should ignore this and NOT error. - // Optional. - Aliases []string `json:"aliases,omitempty"` - // StaticMac for this container. Optional. - StaticMAC HardwareAddr `json:"static_mac,omitempty"` - // InterfaceName for this container. Required in the backend. - // Optional in the frontend. Will be filled with ethX (where X is a integer) when empty. - InterfaceName string `json:"interface_name"` -} - -// NetworkOptions for a given container. -type NetworkOptions struct { - // ContainerID is the container id, used for iptables comments and ipam allocation. - ContainerID string `json:"container_id"` - // ContainerName is the container name, used as dns name. - ContainerName string `json:"container_name"` - // PortMappings contains the port mappings for this container - PortMappings []PortMapping `json:"port_mappings,omitempty"` - // Networks contains all networks with the PerNetworkOptions. - // The map should contain at least one element. - Networks map[string]PerNetworkOptions `json:"networks"` -} - -// PortMapping is one or more ports that will be mapped into the container. -type PortMapping struct { - // HostIP is the IP that we will bind to on the host. - // If unset, assumed to be 0.0.0.0 (all interfaces). - HostIP string `json:"host_ip"` - // ContainerPort is the port number that will be exposed from the - // container. - // Mandatory. - ContainerPort uint16 `json:"container_port"` - // HostPort is the port number that will be forwarded from the host into - // the container. - // If omitted, a random port on the host (guaranteed to be over 1024) - // will be assigned. - HostPort uint16 `json:"host_port"` - // Range is the number of ports that will be forwarded, starting at - // HostPort and ContainerPort and counting up. - // This is 1-indexed, so 1 is assumed to be a single port (only the - // Hostport:Containerport mapping will be added), 2 is two ports (both - // Hostport:Containerport and Hostport+1:Containerport+1), etc. - // If unset, assumed to be 1 (a single port). - // Both hostport + range and containerport + range must be less than - // 65536. - Range uint16 `json:"range"` - // Protocol is the protocol forward. - // Must be either "tcp", "udp", and "sctp", or some combination of these - // separated by commas. - // If unset, assumed to be TCP. - Protocol string `json:"protocol"` -} - -// OCICNIPortMapping maps to the standard CNI portmapping Capability. -// Deprecated, do not use this struct for new fields. This only exists -// for backwards compatibility. -type OCICNIPortMapping struct { - // HostPort is the port number on the host. - HostPort int32 `json:"hostPort"` - // ContainerPort is the port number inside the sandbox. - ContainerPort int32 `json:"containerPort"` - // Protocol is the protocol of the port mapping. - Protocol string `json:"protocol"` - // HostIP is the host ip to use. - HostIP string `json:"hostIP"` -} - -type SetupOptions struct { - NetworkOptions -} - -type TeardownOptions struct { - NetworkOptions -} - -// FilterFunc can be passed to NetworkList to filter the networks. -type FilterFunc func(Network) bool diff --git a/libpod/network/types/network_test.go b/libpod/network/types/network_test.go deleted file mode 100644 index 91ee93692..000000000 --- a/libpod/network/types/network_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package types_test - -import ( - "encoding/json" - "reflect" - "testing" - - "github.com/containers/podman/v3/libpod/network/types" -) - -func TestUnmarshalMacAddress(t *testing.T) { - tests := []struct { - name string - json string - want types.HardwareAddr - wantErr bool - }{ - { - name: "mac as string with colon", - json: `"52:54:00:1c:2e:46"`, - want: types.HardwareAddr{0x52, 0x54, 0x00, 0x1c, 0x2e, 0x46}, - }, - { - name: "mac as string with dash", - json: `"52-54-00-1c-2e-46"`, - want: types.HardwareAddr{0x52, 0x54, 0x00, 0x1c, 0x2e, 0x46}, - }, - { - name: "mac as byte array", - json: `[82, 84, 0, 28, 46, 70]`, - want: types.HardwareAddr{0x52, 0x54, 0x00, 0x1c, 0x2e, 0x46}, - }, - { - name: "null value", - json: `null`, - want: nil, - }, - { - name: "mac as base64", - json: `"qrvM3e7/"`, - want: types.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, - }, - { - name: "invalid string", - json: `"52:54:00:1c:2e`, - wantErr: true, - }, - { - name: "invalid array", - json: `[82, 84, 0, 28, 46`, - wantErr: true, - }, - - { - name: "invalid value", - json: `ab`, - wantErr: true, - }, - { - name: "invalid object", - json: `{}`, - wantErr: true, - }, - } - for _, tt := range tests { - test := tt - t.Run(test.name, func(t *testing.T) { - mac := types.HardwareAddr{} - err := json.Unmarshal([]byte(test.json), &mac) - if (err != nil) != test.wantErr { - t.Errorf("types.HardwareAddress Unmarshal() error = %v, wantErr %v", err, test.wantErr) - return - } - if test.wantErr { - return - } - if !reflect.DeepEqual(mac, test.want) { - t.Errorf("types.HardwareAddress Unmarshal() got = %v, want %v", mac, test.want) - } - }) - } -} diff --git a/libpod/network/util/filters.go b/libpod/network/util/filters.go deleted file mode 100644 index c3c80b352..000000000 --- a/libpod/network/util/filters.go +++ /dev/null @@ -1,79 +0,0 @@ -package util - -import ( - "strings" - - "github.com/containers/podman/v3/libpod/network/types" - "github.com/containers/podman/v3/pkg/util" - "github.com/pkg/errors" -) - -func GenerateNetworkFilters(filters map[string][]string) ([]types.FilterFunc, error) { - filterFuncs := make([]types.FilterFunc, 0, len(filters)) - for key, filterValues := range filters { - filterFunc, err := createFilterFuncs(key, filterValues) - if err != nil { - return nil, err - } - filterFuncs = append(filterFuncs, filterFunc) - } - return filterFuncs, nil -} - -func createFilterFuncs(key string, filterValues []string) (types.FilterFunc, error) { - switch strings.ToLower(key) { - case "name": - // matches one name, regex allowed - return func(net types.Network) bool { - return util.StringMatchRegexSlice(net.Name, filterValues) - }, nil - - case "driver": - // matches network driver - return func(net types.Network) bool { - return util.StringInSlice(net.Driver, filterValues) - }, nil - - case "id": - // matches part of one id - return func(net types.Network) bool { - return util.StringMatchRegexSlice(net.ID, filterValues) - }, nil - - // TODO: add dns enabled, internal filter - } - return createPruneFilterFuncs(key, filterValues) -} - -func GenerateNetworkPruneFilters(filters map[string][]string) ([]types.FilterFunc, error) { - filterFuncs := make([]types.FilterFunc, 0, len(filters)) - for key, filterValues := range filters { - filterFunc, err := createPruneFilterFuncs(key, filterValues) - if err != nil { - return nil, err - } - filterFuncs = append(filterFuncs, filterFunc) - } - return filterFuncs, nil -} - -func createPruneFilterFuncs(key string, filterValues []string) (types.FilterFunc, error) { - switch strings.ToLower(key) { - case "label": - // matches all labels - return func(net types.Network) bool { - return util.MatchLabelFilters(filterValues, net.Labels) - }, nil - - case "until": - until, err := util.ComputeUntilTimestamp(filterValues) - if err != nil { - return nil, err - } - return func(net types.Network) bool { - return net.Created.Before(until) - }, nil - default: - return nil, errors.Errorf("invalid filter %q", key) - } -} diff --git a/libpod/network/util/ip.go b/libpod/network/util/ip.go deleted file mode 100644 index e82b4a781..000000000 --- a/libpod/network/util/ip.go +++ /dev/null @@ -1,56 +0,0 @@ -package util - -import ( - "net" -) - -// IsIPv6 returns true if netIP is IPv6. -func IsIPv6(netIP net.IP) bool { - return netIP != nil && netIP.To4() == nil -} - -// IsIPv4 returns true if netIP is IPv4. -func IsIPv4(netIP net.IP) bool { - return netIP != nil && netIP.To4() != nil -} - -// LastIPInSubnet gets the last IP in a subnet -func LastIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer - // re-parse to ensure clean network address - _, cidr, err := net.ParseCIDR(addr.String()) - if err != nil { - return nil, err - } - - ones, bits := cidr.Mask.Size() - if ones == bits { - return cidr.IP, nil - } - for i := range cidr.IP { - cidr.IP[i] = cidr.IP[i] | ^cidr.Mask[i] - } - return cidr.IP, nil -} - -// FirstIPInSubnet gets the first IP in a subnet -func FirstIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer - // re-parse to ensure clean network address - _, cidr, err := net.ParseCIDR(addr.String()) - if err != nil { - return nil, err - } - ones, bits := cidr.Mask.Size() - if ones == bits { - return cidr.IP, nil - } - cidr.IP[len(cidr.IP)-1]++ - return cidr.IP, nil -} - -// NormalizeIP will transform the given ip to the 4 byte len ipv4 if possible -func NormalizeIP(ip *net.IP) { - ipv4 := ip.To4() - if ipv4 != nil { - *ip = ipv4 - } -} diff --git a/libpod/network/util/ip_calc.go b/libpod/network/util/ip_calc.go deleted file mode 100644 index a27ddf78b..000000000 --- a/libpod/network/util/ip_calc.go +++ /dev/null @@ -1,53 +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 util - -import ( - "math/big" - "net" -) - -// NextIP returns IP incremented by 1 -func NextIP(ip net.IP) net.IP { - i := ipToInt(ip) - return intToIP(i.Add(i, big.NewInt(1))) -} - -// PrevIP returns IP decremented by 1 -func PrevIP(ip net.IP) net.IP { - i := ipToInt(ip) - return intToIP(i.Sub(i, big.NewInt(1))) -} - -// Cmp compares two IPs, returning the usual ordering: -// a < b : -1 -// a == b : 0 -// a > b : 1 -func Cmp(a, b net.IP) int { - aa := ipToInt(a) - bb := ipToInt(b) - return aa.Cmp(bb) -} - -func ipToInt(ip net.IP) *big.Int { - if v := ip.To4(); v != nil { - return big.NewInt(0).SetBytes(v) - } - return big.NewInt(0).SetBytes(ip.To16()) -} - -func intToIP(i *big.Int) net.IP { - return net.IP(i.Bytes()) -} diff --git a/libpod/network/util/ip_test.go b/libpod/network/util/ip_test.go deleted file mode 100644 index 63ac555f0..000000000 --- a/libpod/network/util/ip_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package util - -import ( - "net" - "testing" -) - -func parseCIDR(n string) *net.IPNet { - _, parsedNet, _ := net.ParseCIDR(n) - return parsedNet -} - -func TestFirstIPInSubnet(t *testing.T) { - tests := []struct { - name string - args *net.IPNet - want net.IP - wantErr bool - }{ - {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.0.1"), false}, - {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.1"), false}, - {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.0.1"), false}, - {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.1"), false}, - {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.129"), false}, - {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.0.0.1"), false}, - {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false}, - {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false}, - } - for _, tt := range tests { - test := tt - t.Run(test.name, func(t *testing.T) { - got, err := FirstIPInSubnet(test.args) - if (err != nil) != test.wantErr { - t.Errorf("FirstIPInSubnet() error = %v, wantErr %v", err, test.wantErr) - return - } - if !got.Equal(test.want) { - t.Errorf("FirstIPInSubnet() got = %v, want %v", got, test.want) - } - }) - } -} - -func TestLastIPInSubnet(t *testing.T) { - tests := []struct { - name string - args *net.IPNet - want net.IP - wantErr bool - }{ - {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.255.255"), false}, - {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.255"), false}, - {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.1.255"), false}, - {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.127"), false}, - {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.191"), false}, - {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.255.255.255"), false}, - {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false}, - {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false}, - } - for _, tt := range tests { - test := tt - t.Run(test.name, func(t *testing.T) { - got, err := LastIPInSubnet(test.args) - if (err != nil) != test.wantErr { - t.Errorf("LastIPInSubnet() error = %v, wantErr %v", err, test.wantErr) - return - } - if !got.Equal(test.want) { - t.Errorf("LastIPInSubnet() got = %v, want %v", got, test.want) - } - }) - } -} |