summaryrefslogtreecommitdiff
path: root/libpod/network
diff options
context:
space:
mode:
authorPaul Holzinger <pholzing@redhat.com>2021-08-16 16:11:26 +0200
committerPaul Holzinger <pholzing@redhat.com>2021-09-15 20:00:20 +0200
commit85e8fbf7f33717ef6a0d6cf9e2143b52c874c2de (patch)
tree82b0c29102d2779c18ea8a6f10df5dc1139e3817 /libpod/network
parent218f132fdf4939d9e0374ef860d534f19e71df54 (diff)
downloadpodman-85e8fbf7f33717ef6a0d6cf9e2143b52c874c2de.tar.gz
podman-85e8fbf7f33717ef6a0d6cf9e2143b52c874c2de.tar.bz2
podman-85e8fbf7f33717ef6a0d6cf9e2143b52c874c2de.zip
Wire network interface into libpod
Make use of the new network interface in libpod. This commit contains several breaking changes: - podman network create only outputs the new network name and not file path. - podman network ls shows the network driver instead of the cni version and plugins. - podman network inspect outputs the new network struct and not the cni conflist. - The bindings and libpod api endpoints have been changed to use the new network structure. The container network status is stored in a new field in the state. The status should be received with the new `c.getNetworkStatus`. This will migrate the old status to the new format. Therefore old containers should contine to work correctly in all cases even when network connect/ disconnect is used. New features: - podman network reload keeps the ip and mac for more than one network. - podman container restore keeps the ip and mac for more than one network. - The network create compat endpoint can now use more than one ipam config. The man pages and the swagger doc are updated to reflect the latest changes. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
Diffstat (limited to 'libpod/network')
-rw-r--r--libpod/network/cni/run.go7
-rw-r--r--libpod/network/config.go159
-rw-r--r--libpod/network/create.go310
-rw-r--r--libpod/network/create_test.go130
-rw-r--r--libpod/network/devices.go59
-rw-r--r--libpod/network/files.go211
-rw-r--r--libpod/network/ip.go19
-rw-r--r--libpod/network/lock.go35
-rw-r--r--libpod/network/netconflist.go312
-rw-r--r--libpod/network/netconflist_test.go109
-rw-r--r--libpod/network/network.go288
-rw-r--r--libpod/network/network_test.go35
-rw-r--r--libpod/network/subnet.go78
-rw-r--r--libpod/network/subnet_test.go97
-rw-r--r--libpod/network/types/network.go9
-rw-r--r--libpod/network/util/filters.go40
16 files changed, 42 insertions, 1856 deletions
diff --git a/libpod/network/cni/run.go b/libpod/network/cni/run.go
index 14634262c..b69953c4b 100644
--- a/libpod/network/cni/run.go
+++ b/libpod/network/cni/run.go
@@ -114,7 +114,7 @@ func (n *cniNetwork) Setup(namespacePath string, options types.SetupOptions) (ma
}
logrus.Debugf("cni result for container %s network %s: %v", options.ContainerID, name, cnires)
var status types.StatusBlock
- status, retErr = cniResultToStatus(cnires)
+ status, retErr = CNIResultToStatus(cnires)
if retErr != nil {
return nil, retErr
}
@@ -123,8 +123,9 @@ func (n *cniNetwork) Setup(namespacePath string, options types.SetupOptions) (ma
return results, nil
}
-// cniResultToStatus convert the cni result to status block
-func cniResultToStatus(cniResult *current.Result) (types.StatusBlock, error) {
+// CNIResultToStatus convert the cni result to status block
+// nolint:golint
+func CNIResultToStatus(cniResult *current.Result) (types.StatusBlock, error) {
result := types.StatusBlock{}
nameservers := make([]net.IP, 0, len(cniResult.DNS.Nameservers))
for _, nameserver := range cniResult.DNS.Nameservers {
diff --git a/libpod/network/config.go b/libpod/network/config.go
deleted file mode 100644
index 9a3bc4763..000000000
--- a/libpod/network/config.go
+++ /dev/null
@@ -1,159 +0,0 @@
-package network
-
-import (
- "encoding/json"
- "net"
-
- "github.com/containers/storage/pkg/lockfile"
-)
-
-// TODO once the containers.conf file stuff is worked out, this should be modified
-// to honor defines in the containers.conf as well as overrides?
-
-const (
- // CNIConfigDir is the path where CNI config files exist
- CNIConfigDir = "/etc/cni/net.d"
- // CNIDeviceName is the default network device name and in
- // reality should have an int appended to it (cni-podman4)
- CNIDeviceName = "cni-podman"
- // DefaultPodmanDomainName is used for the dnsname plugin to define
- // a localized domain name for a created network
- DefaultPodmanDomainName = "dns.podman"
- // LockFileName is used for obtaining a lock and is appended
- // to libpod's tmpdir in practice
- LockFileName = "cni.lock"
-)
-
-// CNILock is for preventing name collision and
-// unpredictable results when doing some CNI operations.
-type CNILock struct {
- lockfile.Locker
-}
-
-// GetDefaultPodmanNetwork outputs the default network for podman
-func GetDefaultPodmanNetwork() (*net.IPNet, error) {
- _, n, err := net.ParseCIDR("10.88.1.0/24")
- return n, err
-}
-
-// CNIPlugins is a way of marshalling a CNI network configuration to disk
-type CNIPlugins interface {
- Bytes() ([]byte, error)
-}
-
-// HostLocalBridge describes a configuration for a bridge plugin
-// https://github.com/containernetworking/plugins/tree/master/plugins/main/bridge#network-configuration-reference
-type HostLocalBridge struct {
- PluginType string `json:"type"`
- BrName string `json:"bridge,omitempty"`
- IsGW bool `json:"isGateway"`
- IsDefaultGW bool `json:"isDefaultGateway,omitempty"`
- ForceAddress bool `json:"forceAddress,omitempty"`
- IPMasq bool `json:"ipMasq,omitempty"`
- MTU int `json:"mtu,omitempty"`
- HairpinMode bool `json:"hairpinMode,omitempty"`
- PromiscMode bool `json:"promiscMode,omitempty"`
- Vlan int `json:"vlan,omitempty"`
- IPAM IPAMConfig `json:"ipam"`
-}
-
-// Bytes outputs []byte
-func (h *HostLocalBridge) Bytes() ([]byte, error) {
- return json.MarshalIndent(h, "", "\t")
-}
-
-// IPAMConfig describes an IPAM configuration
-// https://github.com/containernetworking/plugins/tree/master/plugins/ipam/host-local#network-configuration-reference
-type IPAMConfig struct {
- PluginType string `json:"type"`
- Routes []IPAMRoute `json:"routes,omitempty"`
- ResolveConf string `json:"resolveConf,omitempty"`
- DataDir string `json:"dataDir,omitempty"`
- Ranges [][]IPAMLocalHostRangeConf `json:"ranges,omitempty"`
-}
-
-// IPAMLocalHostRangeConf describes the new style IPAM ranges
-type IPAMLocalHostRangeConf struct {
- Subnet string `json:"subnet"`
- RangeStart string `json:"rangeStart,omitempty"`
- RangeEnd string `json:"rangeEnd,omitempty"`
- Gateway string `json:"gateway,omitempty"`
-}
-
-// Bytes outputs the configuration as []byte
-func (i IPAMConfig) Bytes() ([]byte, error) {
- return json.MarshalIndent(i, "", "\t")
-}
-
-// IPAMRoute describes a route in an ipam config
-type IPAMRoute struct {
- Dest string `json:"dst"`
-}
-
-// PortMapConfig describes the default portmapping config
-type PortMapConfig struct {
- PluginType string `json:"type"`
- Capabilities map[string]bool `json:"capabilities"`
-}
-
-// Bytes outputs the configuration as []byte
-func (p PortMapConfig) Bytes() ([]byte, error) {
- return json.MarshalIndent(p, "", "\t")
-}
-
-// MacVLANConfig describes the macvlan config
-type MacVLANConfig struct {
- PluginType string `json:"type"`
- Master string `json:"master"`
- IPAM IPAMConfig `json:"ipam"`
- MTU int `json:"mtu,omitempty"`
-}
-
-// Bytes outputs the configuration as []byte
-func (p MacVLANConfig) Bytes() ([]byte, error) {
- return json.MarshalIndent(p, "", "\t")
-}
-
-// FirewallConfig describes the firewall plugin
-type FirewallConfig struct {
- PluginType string `json:"type"`
- Backend string `json:"backend"`
-}
-
-// Bytes outputs the configuration as []byte
-func (f FirewallConfig) Bytes() ([]byte, error) {
- return json.MarshalIndent(f, "", "\t")
-}
-
-// TuningConfig describes the tuning plugin
-type TuningConfig struct {
- PluginType string `json:"type"`
-}
-
-// Bytes outputs the configuration as []byte
-func (f TuningConfig) Bytes() ([]byte, error) {
- return json.MarshalIndent(f, "", "\t")
-}
-
-// DNSNameConfig describes the dns container name resolution plugin config
-type DNSNameConfig struct {
- PluginType string `json:"type"`
- DomainName string `json:"domainName"`
- Capabilities map[string]bool `json:"capabilities"`
-}
-
-// PodmanMachineConfig enables port handling on the host OS
-type PodmanMachineConfig struct {
- PluginType string `json:"type"`
- Capabilities map[string]bool `json:"capabilities"`
-}
-
-// Bytes outputs the configuration as []byte
-func (d DNSNameConfig) Bytes() ([]byte, error) {
- return json.MarshalIndent(d, "", "\t")
-}
-
-// Bytes outputs the configuration as []byte
-func (p PodmanMachineConfig) Bytes() ([]byte, error) {
- return json.MarshalIndent(p, "", "\t")
-}
diff --git a/libpod/network/create.go b/libpod/network/create.go
deleted file mode 100644
index aca8150b5..000000000
--- a/libpod/network/create.go
+++ /dev/null
@@ -1,310 +0,0 @@
-package network
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "strconv"
-
- "github.com/containernetworking/cni/pkg/version"
- "github.com/containers/common/pkg/config"
- "github.com/containers/podman/v3/pkg/domain/entities"
- "github.com/containers/podman/v3/pkg/util"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-// Create the CNI network
-func Create(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (*entities.NetworkCreateReport, error) {
- var fileName string
- if err := isSupportedDriver(options.Driver); err != nil {
- return nil, err
- }
- // Acquire a lock for CNI
- l, err := acquireCNILock(runtimeConfig)
- if err != nil {
- return nil, err
- }
- defer l.releaseCNILock()
- if len(options.MacVLAN) > 0 || options.Driver == MacVLANNetworkDriver {
- fileName, err = createMacVLAN(name, options, runtimeConfig)
- } else {
- fileName, err = createBridge(name, options, runtimeConfig)
- }
- if err != nil {
- return nil, err
- }
- return &entities.NetworkCreateReport{Filename: fileName}, nil
-}
-
-// validateBridgeOptions validate the bridge networking options
-func validateBridgeOptions(options entities.NetworkCreateOptions) error {
- subnet := &options.Subnet
- ipRange := &options.Range
- gateway := options.Gateway
- // if IPv6 is set an IPv6 subnet MUST be specified
- if options.IPv6 && ((subnet.IP == nil) || (subnet.IP != nil && !IsIPv6(subnet.IP))) {
- return errors.Errorf("ipv6 option requires an IPv6 --subnet to be provided")
- }
- // range and gateway depend on subnet
- if subnet.IP == nil && (ipRange.IP != nil || gateway != nil) {
- return errors.Errorf("every ip-range or gateway must have a corresponding subnet")
- }
-
- // if a range is given, we need to ensure it is "in" the network range.
- if ipRange.IP != nil {
- firstIP, err := FirstIPInSubnet(ipRange)
- if err != nil {
- return errors.Wrapf(err, "failed to get first IP address from ip-range")
- }
- lastIP, err := LastIPInSubnet(ipRange)
- if err != nil {
- return errors.Wrapf(err, "failed to get last IP address from ip-range")
- }
- if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) {
- return errors.Errorf("the ip range %s does not fall within the subnet range %s", ipRange.String(), subnet.String())
- }
- }
-
- // if network is provided and if gateway is provided, make sure it is "in" network
- if gateway != nil && !subnet.Contains(gateway) {
- return errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String())
- }
-
- return nil
-}
-
-// parseMTU parses the mtu option
-func parseMTU(mtu string) (int, error) {
- if mtu == "" {
- return 0, nil // default
- }
- m, err := strconv.Atoi(mtu)
- if err != nil {
- return 0, err
- }
- if m < 0 {
- return 0, errors.Errorf("the value %d for mtu is less than zero", m)
- }
- return m, nil
-}
-
-// parseVlan parses the vlan option
-func parseVlan(vlan string) (int, error) {
- if vlan == "" {
- return 0, nil // default
- }
- return strconv.Atoi(vlan)
-}
-
-// createBridge creates a CNI network
-func createBridge(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) {
- var (
- ipamRanges [][]IPAMLocalHostRangeConf
- err error
- routes []IPAMRoute
- )
- isGateway := true
- ipMasq := true
-
- // validate options
- if err := validateBridgeOptions(options); err != nil {
- return "", err
- }
-
- // For compatibility with the docker implementation:
- // if IPv6 is enabled (it really means dual-stack) then an IPv6 subnet has to be provided, and one free network is allocated for IPv4
- // if IPv6 is not specified the subnet may be specified and can be either IPv4 or IPv6 (podman, unlike docker, allows IPv6 only networks)
- // If not subnet is specified an IPv4 subnet will be allocated
- subnet := &options.Subnet
- ipRange := &options.Range
- gateway := options.Gateway
- if subnet.IP != nil {
- // if network is provided, does it conflict with existing CNI or live networks
- err = ValidateUserNetworkIsAvailable(runtimeConfig, subnet)
- if err != nil {
- return "", err
- }
- // obtain CNI subnet default route
- defaultRoute, err := NewIPAMDefaultRoute(IsIPv6(subnet.IP))
- if err != nil {
- return "", err
- }
- routes = append(routes, defaultRoute)
- // obtain CNI range
- ipamRange, err := NewIPAMLocalHostRange(subnet, ipRange, gateway)
- if err != nil {
- return "", err
- }
- ipamRanges = append(ipamRanges, ipamRange)
- }
- // if no network is provided or IPv6 flag used, figure out the IPv4 network
- if options.IPv6 || len(routes) == 0 {
- subnetV4, err := GetFreeNetwork(runtimeConfig)
- if err != nil {
- return "", err
- }
- // obtain IPv4 default route
- defaultRoute, err := NewIPAMDefaultRoute(false)
- if err != nil {
- return "", err
- }
- routes = append(routes, defaultRoute)
- // the CNI bridge plugin does not need to set
- // the range or gateway options explicitly
- ipamRange, err := NewIPAMLocalHostRange(subnetV4, nil, nil)
- if err != nil {
- return "", err
- }
- ipamRanges = append(ipamRanges, ipamRange)
- }
-
- // create CNI config
- ipamConfig, err := NewIPAMHostLocalConf(routes, ipamRanges)
- if err != nil {
- return "", err
- }
-
- if options.Internal {
- isGateway = false
- ipMasq = false
- }
-
- var mtu int
- var vlan int
- for k, v := range options.Options {
- var err error
- switch k {
- case "mtu":
- mtu, err = parseMTU(v)
- if err != nil {
- return "", err
- }
-
- case "vlan":
- vlan, err = parseVlan(v)
- if err != nil {
- return "", err
- }
-
- default:
- return "", errors.Errorf("unsupported option %s", k)
- }
- }
-
- // obtain host bridge name
- bridgeDeviceName, err := GetFreeDeviceName(runtimeConfig)
- if err != nil {
- return "", err
- }
-
- if len(name) > 0 {
- netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig)
- if err != nil {
- return "", err
- }
- if util.StringInSlice(name, netNames) {
- return "", errors.Errorf("the network name %s is already used", name)
- }
- } else {
- // If no name is given, we give the name of the bridge device
- name = bridgeDeviceName
- }
-
- // create CNI plugin configuration
- ncList := NewNcList(name, version.Current(), options.Labels)
- var plugins []CNIPlugins
- // TODO need to iron out the role of isDefaultGW and IPMasq
- bridge := NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, mtu, vlan, ipamConfig)
- plugins = append(plugins, bridge)
- plugins = append(plugins, NewPortMapPlugin())
- plugins = append(plugins, NewFirewallPlugin())
- plugins = append(plugins, NewTuningPlugin())
- // if we find the dnsname plugin we add configuration for it
- if HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) && !options.DisableDNS {
- if options.Internal {
- logrus.Warnf("dnsname and --internal networks are incompatible. dnsname plugin not configured for network %s", name)
- } else {
- // Note: in the future we might like to allow for dynamic domain names
- plugins = append(plugins, NewDNSNamePlugin(DefaultPodmanDomainName))
- }
- }
- // Add the podman-machine CNI plugin if we are in a machine
- if runtimeConfig.MachineEnabled() { // check if we are in a machine vm
- plugins = append(plugins, NewPodmanMachinePlugin())
- }
- ncList["plugins"] = plugins
- b, err := json.MarshalIndent(ncList, "", " ")
- if err != nil {
- return "", err
- }
- if err := os.MkdirAll(GetCNIConfDir(runtimeConfig), 0755); err != nil {
- return "", err
- }
- cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name))
- err = ioutil.WriteFile(cniPathName, b, 0644)
- return cniPathName, err
-}
-
-func createMacVLAN(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) {
- var (
- mtu int
- plugins []CNIPlugins
- )
- liveNetNames, err := GetLiveNetworkNames()
- if err != nil {
- return "", err
- }
-
- // The parent can be defined with --macvlan or as an option (-o parent:device)
- parentNetworkDevice := options.MacVLAN
- if len(parentNetworkDevice) < 1 {
- if parent, ok := options.Options["parent"]; ok {
- parentNetworkDevice = parent
- }
- }
-
- // Make sure the host-device exists if provided
- if len(parentNetworkDevice) > 0 && !util.StringInSlice(parentNetworkDevice, liveNetNames) {
- return "", errors.Errorf("failed to find network interface %q", parentNetworkDevice)
- }
- if len(name) > 0 {
- netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig)
- if err != nil {
- return "", err
- }
- if util.StringInSlice(name, netNames) {
- return "", errors.Errorf("the network name %s is already used", name)
- }
- } else {
- name, err = GetFreeDeviceName(runtimeConfig)
- if err != nil {
- return "", err
- }
- }
- ncList := NewNcList(name, version.Current(), options.Labels)
- if val, ok := options.Options["mtu"]; ok {
- intVal, err := strconv.Atoi(val)
- if err != nil {
- return "", err
- }
- if intVal > 0 {
- mtu = intVal
- }
- }
- macvlan, err := NewMacVLANPlugin(parentNetworkDevice, options.Gateway, &options.Range, &options.Subnet, mtu)
- if err != nil {
- return "", err
- }
- plugins = append(plugins, macvlan)
- ncList["plugins"] = plugins
- b, err := json.MarshalIndent(ncList, "", " ")
- if err != nil {
- return "", err
- }
- cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name))
- err = ioutil.WriteFile(cniPathName, b, 0644)
- return cniPathName, err
-}
diff --git a/libpod/network/create_test.go b/libpod/network/create_test.go
deleted file mode 100644
index c3824bd91..000000000
--- a/libpod/network/create_test.go
+++ /dev/null
@@ -1,130 +0,0 @@
-package network
-
-import (
- "net"
- "testing"
-
- "github.com/containers/podman/v3/pkg/domain/entities"
-)
-
-func Test_validateBridgeOptions(t *testing.T) {
- tests := []struct {
- name string
- subnet net.IPNet
- ipRange net.IPNet
- gateway net.IP
- isIPv6 bool
- wantErr bool
- }{
- {
- name: "IPv4 subnet only",
- subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- },
- {
- name: "IPv4 subnet and range",
- subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
- },
- {
- name: "IPv4 subnet and gateway",
- subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- gateway: net.ParseIP("192.168.0.10"),
- },
- {
- name: "IPv4 subnet, range and gateway",
- subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
- gateway: net.ParseIP("192.168.0.10"),
- },
- {
- name: "IPv6 subnet only",
- subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- },
- {
- name: "IPv6 subnet and range",
- subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- ipRange: net.IPNet{IP: net.ParseIP("2001:DB8:0:0:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))},
- isIPv6: true,
- },
- {
- name: "IPv6 subnet and gateway",
- subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- gateway: net.ParseIP("2001:DB8::2"),
- isIPv6: true,
- },
- {
- name: "IPv6 subnet, range and gateway",
- subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- ipRange: net.IPNet{IP: net.ParseIP("2001:DB8:0:0:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))},
- gateway: net.ParseIP("2001:DB8::2"),
- isIPv6: true,
- },
- {
- name: "IPv6 subnet, range and gateway without IPv6 option (PODMAN SUPPORTS IT UNLIKE DOCKER)",
- subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- ipRange: net.IPNet{IP: net.ParseIP("2001:DB8:0:0:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))},
- gateway: net.ParseIP("2001:DB8::2"),
- isIPv6: false,
- },
- {
- name: "range provided but not subnet",
- ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
- wantErr: true,
- },
- {
- name: "gateway provided but not subnet",
- gateway: net.ParseIP("192.168.0.10"),
- wantErr: true,
- },
- {
- name: "IPv4 subnet but IPv6 required",
- subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
- gateway: net.ParseIP("192.168.0.10"),
- isIPv6: true,
- wantErr: true,
- },
- {
- name: "IPv6 required but IPv4 options used",
- subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
- gateway: net.ParseIP("192.168.0.10"),
- isIPv6: true,
- wantErr: true,
- },
- {
- name: "IPv6 required but not subnet provided",
- isIPv6: true,
- wantErr: true,
- },
- {
- name: "range out of the subnet",
- subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- ipRange: net.IPNet{IP: net.ParseIP("2001:1:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))},
- gateway: net.ParseIP("2001:DB8::2"),
- isIPv6: true,
- wantErr: true,
- },
- {
- name: "gateway out of the subnet",
- subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- gateway: net.ParseIP("2001::2"),
- isIPv6: true,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- tt := tt
- t.Run(tt.name, func(t *testing.T) {
- options := entities.NetworkCreateOptions{
- Subnet: tt.subnet,
- Range: tt.ipRange,
- Gateway: tt.gateway,
- IPv6: tt.isIPv6,
- }
- if err := validateBridgeOptions(options); (err != nil) != tt.wantErr {
- t.Errorf("validateBridgeOptions() error = %v, wantErr %v", err, tt.wantErr)
- }
- })
- }
-}
diff --git a/libpod/network/devices.go b/libpod/network/devices.go
deleted file mode 100644
index fc9aff337..000000000
--- a/libpod/network/devices.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package network
-
-import (
- "fmt"
-
- "github.com/containers/common/pkg/config"
- "github.com/containers/podman/v3/pkg/util"
- "github.com/sirupsen/logrus"
- "github.com/vishvananda/netlink"
-)
-
-// GetFreeDeviceName returns a device name that is unused; used when no network
-// name is provided by user
-func GetFreeDeviceName(config *config.Config) (string, error) {
- var (
- deviceNum uint
- deviceName string
- )
- networkNames, err := GetNetworkNamesFromFileSystem(config)
- if err != nil {
- return "", err
- }
- liveNetworksNames, err := GetLiveNetworkNames()
- if err != nil {
- return "", err
- }
- bridgeNames, err := GetBridgeNamesFromFileSystem(config)
- if err != nil {
- return "", err
- }
- for {
- deviceName = fmt.Sprintf("%s%d", CNIDeviceName, deviceNum)
- logrus.Debugf("checking if device name %q exists in other cni networks", deviceName)
- if util.StringInSlice(deviceName, networkNames) {
- deviceNum++
- continue
- }
- logrus.Debugf("checking if device name %q exists in live networks", deviceName)
- if util.StringInSlice(deviceName, liveNetworksNames) {
- deviceNum++
- continue
- }
- logrus.Debugf("checking if device name %q already exists as a bridge name ", deviceName)
- if !util.StringInSlice(deviceName, bridgeNames) {
- break
- }
- deviceNum++
- }
- return deviceName, nil
-}
-
-// RemoveInterface removes an interface by the given name
-func RemoveInterface(interfaceName string) error {
- link, err := netlink.LinkByName(interfaceName)
- if err != nil {
- return err
- }
- return netlink.LinkDel(link)
-}
diff --git a/libpod/network/files.go b/libpod/network/files.go
deleted file mode 100644
index d876113f9..000000000
--- a/libpod/network/files.go
+++ /dev/null
@@ -1,211 +0,0 @@
-package network
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "sort"
- "strings"
-
- "github.com/containernetworking/cni/libcni"
- "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
- "github.com/containers/common/pkg/config"
- "github.com/containers/podman/v3/libpod/define"
- "github.com/containers/podman/v3/pkg/network"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-// ErrNoSuchNetworkInterface indicates that no network interface exists
-var ErrNoSuchNetworkInterface = errors.New("unable to find interface name for network")
-
-// GetCNIConfDir get CNI configuration directory
-func GetCNIConfDir(configArg *config.Config) string {
- if len(configArg.Network.NetworkConfigDir) < 1 {
- dc, err := config.DefaultConfig()
- if err != nil {
- // Fallback to hard-coded dir
- return CNIConfigDir
- }
- return dc.Network.NetworkConfigDir
- }
- return configArg.Network.NetworkConfigDir
-}
-
-// LoadCNIConfsFromDir loads all the CNI configurations from a dir
-func LoadCNIConfsFromDir(dir string) ([]*libcni.NetworkConfigList, error) {
- files, err := libcni.ConfFiles(dir, []string{".conflist"})
- if err != nil {
- return nil, err
- }
- sort.Strings(files)
-
- configs := make([]*libcni.NetworkConfigList, 0, len(files))
- for _, confFile := range files {
- conf, err := libcni.ConfListFromFile(confFile)
- if err != nil {
- return nil, errors.Wrapf(err, "in %s", confFile)
- }
- configs = append(configs, conf)
- }
- return configs, nil
-}
-
-// GetCNIConfigPathByNameOrID finds a CNI network by name and
-// returns its configuration file path
-func GetCNIConfigPathByNameOrID(config *config.Config, name string) (string, error) {
- files, err := libcni.ConfFiles(GetCNIConfDir(config), []string{".conflist"})
- if err != nil {
- return "", err
- }
- idMatch := 0
- file := ""
- for _, confFile := range files {
- conf, err := libcni.ConfListFromFile(confFile)
- if err != nil {
- return "", errors.Wrapf(err, "in %s", confFile)
- }
- if conf.Name == name {
- return confFile, nil
- }
- if strings.HasPrefix(network.GetNetworkID(conf.Name), name) {
- idMatch++
- file = confFile
- }
- }
- if idMatch == 1 {
- return file, nil
- }
- if idMatch > 1 {
- return "", errors.Errorf("more than one result for network ID %s", name)
- }
- return "", errors.Wrap(define.ErrNoSuchNetwork, fmt.Sprintf("unable to find network configuration for %s", name))
-}
-
-// ReadRawCNIConfByNameOrID reads the raw CNI configuration for a CNI
-// network by name
-func ReadRawCNIConfByNameOrID(config *config.Config, name string) ([]byte, error) {
- confFile, err := GetCNIConfigPathByNameOrID(config, name)
- if err != nil {
- return nil, err
- }
- b, err := ioutil.ReadFile(confFile)
- return b, err
-}
-
-// GetNetworkLabels returns a list of labels as a string
-func GetNetworkLabels(list *libcni.NetworkConfigList) NcLabels {
- cniJSON := make(map[string]interface{})
- err := json.Unmarshal(list.Bytes, &cniJSON)
- if err != nil {
- logrus.Errorf("failed to unmarshal network config %v %v", cniJSON["name"], err)
- return nil
- }
- if args, ok := cniJSON["args"]; ok {
- if key, ok := args.(map[string]interface{}); ok {
- if labels, ok := key[PodmanLabelKey]; ok {
- if labels, ok := labels.(map[string]interface{}); ok {
- result := make(NcLabels, len(labels))
- for k, v := range labels {
- if v, ok := v.(string); ok {
- result[k] = v
- } else {
- logrus.Errorf("network config %v invalid label value type %T should be string", cniJSON["name"], labels)
- }
- }
- return result
- }
- logrus.Errorf("network config %v invalid label type %T should be map[string]string", cniJSON["name"], labels)
- }
- }
- }
- return nil
-}
-
-// GetNetworksFromFilesystem gets all the networks from the cni configuration
-// files
-func GetNetworksFromFilesystem(config *config.Config) ([]*allocator.Net, error) {
- var cniNetworks []*allocator.Net
-
- networks, err := LoadCNIConfsFromDir(GetCNIConfDir(config))
- if err != nil {
- return nil, err
- }
- for _, n := range networks {
- for _, cniplugin := range n.Plugins {
- if cniplugin.Network.Type == "bridge" {
- ipamConf := allocator.Net{}
- if err := json.Unmarshal(cniplugin.Bytes, &ipamConf); err != nil {
- return nil, err
- }
- cniNetworks = append(cniNetworks, &ipamConf)
- break
- }
- }
- }
- return cniNetworks, nil
-}
-
-// GetNetworkNamesFromFileSystem gets all the names from the cni network
-// configuration files
-func GetNetworkNamesFromFileSystem(config *config.Config) ([]string, error) {
- networks, err := LoadCNIConfsFromDir(GetCNIConfDir(config))
- if err != nil {
- return nil, err
- }
- networkNames := []string{}
- for _, n := range networks {
- networkNames = append(networkNames, n.Name)
- }
- return networkNames, nil
-}
-
-// GetInterfaceNameFromConfig returns the interface name for the bridge plugin
-func GetInterfaceNameFromConfig(path string) (string, error) {
- var name string
- conf, err := libcni.ConfListFromFile(path)
- if err != nil {
- return "", err
- }
- for _, cniplugin := range conf.Plugins {
- if cniplugin.Network.Type == "bridge" {
- plugin := make(map[string]interface{})
- if err := json.Unmarshal(cniplugin.Bytes, &plugin); err != nil {
- return "", err
- }
- name = plugin["bridge"].(string)
- break
- }
- }
- if len(name) == 0 {
- return "", ErrNoSuchNetworkInterface
- }
- return name, nil
-}
-
-// GetBridgeNamesFromFileSystem is a convenience function to get all the bridge
-// names from the configured networks
-func GetBridgeNamesFromFileSystem(config *config.Config) ([]string, error) {
- networks, err := LoadCNIConfsFromDir(GetCNIConfDir(config))
- if err != nil {
- return nil, err
- }
-
- bridgeNames := []string{}
- for _, n := range networks {
- var name string
- // iterate network conflists
- for _, cniplugin := range n.Plugins {
- // iterate plugins
- if cniplugin.Network.Type == "bridge" {
- plugin := make(map[string]interface{})
- if err := json.Unmarshal(cniplugin.Bytes, &plugin); err != nil {
- continue
- }
- name = plugin["bridge"].(string)
- }
- }
- bridgeNames = append(bridgeNames, name)
- }
- return bridgeNames, nil
-}
diff --git a/libpod/network/ip.go b/libpod/network/ip.go
deleted file mode 100644
index ba93a0d05..000000000
--- a/libpod/network/ip.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package network
-
-import (
- "net"
-
- "github.com/containernetworking/plugins/pkg/ip"
-)
-
-// CalcGatewayIP takes a network and returns the first IP in it.
-func CalcGatewayIP(ipn *net.IPNet) net.IP {
- // taken from cni bridge plugin as it is not exported
- nid := ipn.IP.Mask(ipn.Mask)
- return ip.NextIP(nid)
-}
-
-// IsIPv6 returns if netIP is IPv6.
-func IsIPv6(netIP net.IP) bool {
- return netIP != nil && netIP.To4() == nil
-}
diff --git a/libpod/network/lock.go b/libpod/network/lock.go
deleted file mode 100644
index 037f41efa..000000000
--- a/libpod/network/lock.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package network
-
-import (
- "os"
- "path/filepath"
-
- "github.com/containers/common/pkg/config"
- "github.com/containers/storage"
-)
-
-// acquireCNILock gets a lock that should be used in create and
-// delete cases to avoid unwanted collisions in network names.
-// TODO this uses a file lock and should be converted to shared memory
-// when we have a more general shared memory lock in libpod
-func acquireCNILock(config *config.Config) (*CNILock, error) {
- cniDir := GetCNIConfDir(config)
- err := os.MkdirAll(cniDir, 0755)
- if err != nil {
- return nil, err
- }
- l, err := storage.GetLockfile(filepath.Join(cniDir, LockFileName))
- if err != nil {
- return nil, err
- }
- l.Lock()
- cnilock := CNILock{
- Locker: l,
- }
- return &cnilock, nil
-}
-
-// ReleaseCNILock unlocks the previously held lock
-func (l *CNILock) releaseCNILock() {
- l.Unlock()
-}
diff --git a/libpod/network/netconflist.go b/libpod/network/netconflist.go
deleted file mode 100644
index d6c33740e..000000000
--- a/libpod/network/netconflist.go
+++ /dev/null
@@ -1,312 +0,0 @@
-package network
-
-import (
- "net"
- "os"
- "path/filepath"
- "strings"
- "syscall"
- "time"
-
- "github.com/containernetworking/cni/libcni"
- "github.com/containers/common/pkg/config"
- "github.com/containers/podman/v3/pkg/network"
- "github.com/containers/podman/v3/pkg/util"
- "github.com/pkg/errors"
-)
-
-const (
- defaultIPv4Route = "0.0.0.0/0"
- defaultIPv6Route = "::/0"
-)
-
-// NcList describes a generic map
-type NcList map[string]interface{}
-
-// NcArgs describes the cni args field
-type NcArgs map[string]NcLabels
-
-// NcLabels describes the label map
-type NcLabels map[string]string
-
-// PodmanLabelKey key used to store the podman network label in a cni config
-const PodmanLabelKey = "podman_labels"
-
-// NewNcList creates a generic map of values with string
-// keys and adds in version and network name
-func NewNcList(name, version string, labels NcLabels) NcList {
- n := NcList{}
- n["cniVersion"] = version
- n["name"] = name
- if len(labels) > 0 {
- n["args"] = NcArgs{PodmanLabelKey: labels}
- }
- return n
-}
-
-// NewHostLocalBridge creates a new LocalBridge for host-local
-func NewHostLocalBridge(name string, isGateWay, isDefaultGW, ipMasq bool, mtu int, vlan int, ipamConf IPAMConfig) *HostLocalBridge {
- hostLocalBridge := HostLocalBridge{
- PluginType: "bridge",
- BrName: name,
- IPMasq: ipMasq,
- MTU: mtu,
- HairpinMode: true,
- Vlan: vlan,
- IPAM: ipamConf,
- }
- if isGateWay {
- hostLocalBridge.IsGW = true
- }
- if isDefaultGW {
- hostLocalBridge.IsDefaultGW = true
- }
- return &hostLocalBridge
-}
-
-// NewIPAMHostLocalConf creates a new IPAMHostLocal configuration
-func NewIPAMHostLocalConf(routes []IPAMRoute, ipamRanges [][]IPAMLocalHostRangeConf) (IPAMConfig, error) {
- ipamConf := IPAMConfig{
- PluginType: "host-local",
- Routes: routes,
- // Possible future support ? Leaving for clues
- //ResolveConf: "",
- //DataDir: ""
- }
-
- ipamConf.Ranges = ipamRanges
- return ipamConf, nil
-}
-
-// NewIPAMLocalHostRange create a new IPAM range
-func NewIPAMLocalHostRange(subnet *net.IPNet, ipRange *net.IPNet, gw net.IP) ([]IPAMLocalHostRangeConf, error) { //nolint:interfacer
- var ranges []IPAMLocalHostRangeConf
- hostRange := IPAMLocalHostRangeConf{
- Subnet: subnet.String(),
- }
- // an user provided a range, we add it here
- if ipRange != nil && ipRange.IP != nil {
- first, err := FirstIPInSubnet(ipRange)
- if err != nil {
- return nil, err
- }
- last, err := LastIPInSubnet(ipRange)
- if err != nil {
- return nil, err
- }
- hostRange.RangeStart = first.String()
- hostRange.RangeEnd = last.String()
- }
- if gw != nil {
- hostRange.Gateway = gw.String()
- } else {
- // Add first ip in subnet as gateway. It is not required
- // by cni but should be included because of network inspect.
- hostRange.Gateway = CalcGatewayIP(subnet).String()
- }
- ranges = append(ranges, hostRange)
- return ranges, nil
-}
-
-// NewIPAMRoute creates a new IPAM route configuration
-func NewIPAMRoute(r *net.IPNet) IPAMRoute { //nolint:interfacer
- return IPAMRoute{Dest: r.String()}
-}
-
-// NewIPAMDefaultRoute creates a new IPAMDefault route of
-// 0.0.0.0/0 for IPv4 or ::/0 for IPv6
-func NewIPAMDefaultRoute(isIPv6 bool) (IPAMRoute, error) {
- route := defaultIPv4Route
- if isIPv6 {
- route = defaultIPv6Route
- }
- _, n, err := net.ParseCIDR(route)
- if err != nil {
- return IPAMRoute{}, err
- }
- return NewIPAMRoute(n), nil
-}
-
-// NewPortMapPlugin creates a predefined, default portmapping
-// configuration
-func NewPortMapPlugin() PortMapConfig {
- caps := make(map[string]bool)
- caps["portMappings"] = true
- p := PortMapConfig{
- PluginType: "portmap",
- Capabilities: caps,
- }
- return p
-}
-
-// NewFirewallPlugin creates a generic firewall plugin
-func NewFirewallPlugin() FirewallConfig {
- return FirewallConfig{
- PluginType: "firewall",
- }
-}
-
-// NewTuningPlugin creates a generic tuning section
-func NewTuningPlugin() TuningConfig {
- return TuningConfig{
- PluginType: "tuning",
- }
-}
-
-// NewDNSNamePlugin creates the dnsname config with a given
-// domainname
-func NewDNSNamePlugin(domainName string) DNSNameConfig {
- caps := make(map[string]bool, 1)
- caps["aliases"] = true
- return DNSNameConfig{
- PluginType: "dnsname",
- DomainName: domainName,
- Capabilities: caps,
- }
-}
-
-// HasDNSNamePlugin looks to see if the dnsname cni plugin is present
-func HasDNSNamePlugin(paths []string) bool {
- for _, p := range paths {
- if _, err := os.Stat(filepath.Join(p, "dnsname")); err == nil {
- return true
- }
- }
- return false
-}
-
-// NewMacVLANPlugin creates a macvlanconfig with a given device name
-func NewMacVLANPlugin(device string, gateway net.IP, ipRange *net.IPNet, subnet *net.IPNet, mtu int) (MacVLANConfig, error) {
- i := IPAMConfig{PluginType: "dhcp"}
- if gateway != nil ||
- (ipRange != nil && ipRange.IP != nil && ipRange.Mask != nil) ||
- (subnet != nil && subnet.IP != nil && subnet.Mask != nil) {
- ipam, err := NewIPAMLocalHostRange(subnet, ipRange, gateway)
- if err != nil {
- return MacVLANConfig{}, err
- }
- ranges := make([][]IPAMLocalHostRangeConf, 0)
- ranges = append(ranges, ipam)
- i.Ranges = ranges
- route, err := NewIPAMDefaultRoute(IsIPv6(subnet.IP))
- if err != nil {
- return MacVLANConfig{}, err
- }
- i.Routes = []IPAMRoute{route}
- i.PluginType = "host-local"
- }
-
- m := MacVLANConfig{
- PluginType: "macvlan",
- IPAM: i,
- }
- if mtu > 0 {
- m.MTU = mtu
- }
- // CNI is supposed to use the default route if a
- // parent device is not provided
- if len(device) > 0 {
- m.Master = device
- }
- return m, nil
-}
-
-// IfPassesFilter filters NetworkListReport and returns true if the filter match the given config
-func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]string) (bool, error) {
- result := true
- for key, filterValues := range filters {
- result = false
- switch strings.ToLower(key) {
- case "name":
- // matches one name, regex allowed
- result = util.StringMatchRegexSlice(netconf.Name, filterValues)
-
- case "plugin":
- // match one plugin
- plugins := network.GetCNIPlugins(netconf)
- for _, val := range filterValues {
- if strings.Contains(plugins, val) {
- result = true
- break
- }
- }
-
- case "label":
- // matches all labels
- result = util.MatchLabelFilters(filterValues, GetNetworkLabels(netconf))
-
- case "driver":
- // matches only for the DefaultNetworkDriver
- for _, filterValue := range filterValues {
- plugins := network.GetCNIPlugins(netconf)
- if filterValue == DefaultNetworkDriver &&
- strings.Contains(plugins, DefaultNetworkDriver) {
- result = true
- }
- }
-
- case "id":
- // matches part of one id
- for _, filterValue := range filterValues {
- if strings.Contains(network.GetNetworkID(netconf.Name), filterValue) {
- result = true
- break
- }
- }
-
- // TODO: add dangling filter
-
- default:
- return false, errors.Errorf("invalid filter %q", key)
- }
- }
- return result, nil
-}
-
-// IfPassesPruneFilter filters NetworkListReport and returns true if the prune filter match the given config
-func IfPassesPruneFilter(config *config.Config, netconf *libcni.NetworkConfigList, f map[string][]string) (bool, error) {
- for key, filterValues := range f {
- switch strings.ToLower(key) {
- case "label":
- return util.MatchLabelFilters(filterValues, GetNetworkLabels(netconf)), nil
- case "until":
- until, err := util.ComputeUntilTimestamp(filterValues)
- if err != nil {
- return false, err
- }
- created, err := getCreatedTimestamp(config, netconf)
- if err != nil {
- return false, err
- }
- if created.Before(until) {
- return true, nil
- }
- default:
- return false, errors.Errorf("invalid filter %q", key)
- }
- }
- return false, nil
-}
-
-func getCreatedTimestamp(config *config.Config, netconf *libcni.NetworkConfigList) (*time.Time, error) {
- networkConfigPath, err := GetCNIConfigPathByNameOrID(config, netconf.Name)
- if err != nil {
- return nil, err
- }
- f, err := os.Stat(networkConfigPath)
- if err != nil {
- return nil, err
- }
- stat := f.Sys().(*syscall.Stat_t)
- created := time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)) // nolint: unconvert
- return &created, nil
-}
-
-func NewPodmanMachinePlugin() PodmanMachineConfig {
- caps := make(map[string]bool, 1)
- caps["portMappings"] = true
- return PodmanMachineConfig{
- PluginType: "podman-machine",
- Capabilities: caps,
- }
-}
diff --git a/libpod/network/netconflist_test.go b/libpod/network/netconflist_test.go
deleted file mode 100644
index 161764ed9..000000000
--- a/libpod/network/netconflist_test.go
+++ /dev/null
@@ -1,109 +0,0 @@
-package network
-
-import (
- "net"
- "reflect"
- "testing"
-)
-
-func TestNewIPAMDefaultRoute(t *testing.T) {
- tests := []struct {
- name string
- isIPv6 bool
- want IPAMRoute
- }{
- {
- name: "IPv4 default route",
- isIPv6: false,
- want: IPAMRoute{defaultIPv4Route},
- },
- {
- name: "IPv6 default route",
- isIPv6: true,
- want: IPAMRoute{defaultIPv6Route},
- },
- }
- for _, tt := range tests {
- tt := tt
- t.Run(tt.name, func(t *testing.T) {
- got, err := NewIPAMDefaultRoute(tt.isIPv6)
- if err != nil {
- t.Errorf("no error expected: %v", err)
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("NewIPAMDefaultRoute() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestNewIPAMLocalHostRange(t *testing.T) {
- tests := []struct {
- name string
- subnet *net.IPNet
- ipRange *net.IPNet
- gw net.IP
- want []IPAMLocalHostRangeConf
- }{
- {
- name: "IPv4 subnet",
- subnet: &net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- want: []IPAMLocalHostRangeConf{
- {
- Subnet: "192.168.0.0/24",
- Gateway: "192.168.0.1",
- },
- },
- },
- {
- name: "IPv4 subnet, range and gateway",
- subnet: &net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- ipRange: &net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
- gw: net.ParseIP("192.168.0.10"),
- want: []IPAMLocalHostRangeConf{
- {
- Subnet: "192.168.0.0/24",
- RangeStart: "192.168.0.129",
- RangeEnd: "192.168.0.255",
- Gateway: "192.168.0.10",
- },
- },
- },
- {
- name: "IPv6 subnet",
- subnet: &net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- want: []IPAMLocalHostRangeConf{
- {
- Subnet: "2001:db8::/48",
- Gateway: "2001:db8::1",
- },
- },
- },
- {
- name: "IPv6 subnet, range and gateway",
- subnet: &net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- ipRange: &net.IPNet{IP: net.ParseIP("2001:DB8:1:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))},
- gw: net.ParseIP("2001:DB8::2"),
- want: []IPAMLocalHostRangeConf{
- {
- Subnet: "2001:db8::/48",
- RangeStart: "2001:db8:1:1::1",
- RangeEnd: "2001:db8:1:1:ffff:ffff:ffff:ffff",
- Gateway: "2001:db8::2",
- },
- },
- },
- }
- for _, tt := range tests {
- tt := tt
- t.Run(tt.name, func(t *testing.T) {
- got, err := NewIPAMLocalHostRange(tt.subnet, tt.ipRange, tt.gw)
- if err != nil {
- t.Errorf("no error expected: %v", err)
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("NewIPAMLocalHostRange() = %v, want %v", got, tt.want)
- }
- })
- }
-}
diff --git a/libpod/network/network.go b/libpod/network/network.go
deleted file mode 100644
index 3b81ce776..000000000
--- a/libpod/network/network.go
+++ /dev/null
@@ -1,288 +0,0 @@
-package network
-
-import (
- "encoding/json"
- "net"
- "os"
-
- "github.com/containernetworking/cni/libcni"
- "github.com/containernetworking/cni/pkg/types"
- "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
- "github.com/containers/common/pkg/config"
- "github.com/containers/podman/v3/libpod/define"
- "github.com/containers/podman/v3/pkg/domain/entities"
- "github.com/containers/podman/v3/pkg/rootless"
- "github.com/containers/podman/v3/pkg/util"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-var (
- // BridgeNetworkDriver defines the bridge cni driver
- BridgeNetworkDriver = "bridge"
- // DefaultNetworkDriver is the default network type used
- DefaultNetworkDriver = BridgeNetworkDriver
- // MacVLANNetworkDriver defines the macvlan cni driver
- MacVLANNetworkDriver = "macvlan"
-)
-
-// SupportedNetworkDrivers describes the list of supported drivers
-var SupportedNetworkDrivers = []string{BridgeNetworkDriver, MacVLANNetworkDriver}
-
-// isSupportedDriver checks if the user provided driver is supported
-func isSupportedDriver(driver string) error {
- if util.StringInSlice(driver, SupportedNetworkDrivers) {
- return nil
- }
- return errors.Errorf("driver '%s' is not supported", driver)
-}
-
-// GetLiveNetworks returns a slice of networks representing what the system
-// has defined as network interfaces
-func GetLiveNetworks() ([]*net.IPNet, error) {
- addrs, err := net.InterfaceAddrs()
- if err != nil {
- return nil, err
- }
- nets := make([]*net.IPNet, 0, len(addrs))
- for _, address := range addrs {
- _, n, err := net.ParseCIDR(address.String())
- if err != nil {
- return nil, err
- }
- nets = append(nets, n)
- }
- return nets, nil
-}
-
-// GetLiveNetworkNames returns a list of network interfaces on the system
-func GetLiveNetworkNames() ([]string, error) {
- liveInterfaces, err := net.Interfaces()
- if err != nil {
- return nil, err
- }
- interfaceNames := make([]string, 0, len(liveInterfaces))
- for _, i := range liveInterfaces {
- interfaceNames = append(interfaceNames, i.Name)
- }
- return interfaceNames, nil
-}
-
-// GetFreeNetwork looks for a free network according to existing cni configuration
-// files and network interfaces.
-func GetFreeNetwork(config *config.Config) (*net.IPNet, error) {
- networks, err := GetNetworksFromFilesystem(config)
- if err != nil {
- return nil, err
- }
- liveNetworks, err := GetLiveNetworks()
- if err != nil {
- return nil, err
- }
- nextNetwork, err := GetDefaultPodmanNetwork()
- if err != nil {
- return nil, err
- }
- logrus.Debugf("default network is %s", nextNetwork.String())
- for {
- newNetwork, err := NextSubnet(nextNetwork)
- if err != nil {
- return nil, err
- }
- logrus.Debugf("checking if network %s intersects with other cni networks", nextNetwork.String())
- if intersectsConfig, _ := networkIntersectsWithNetworks(newNetwork, allocatorToIPNets(networks)); intersectsConfig {
- logrus.Debugf("network %s is already being used by a cni configuration", nextNetwork.String())
- nextNetwork = newNetwork
- continue
- }
- logrus.Debugf("checking if network %s intersects with any network interfaces", nextNetwork.String())
- if intersectsLive, _ := networkIntersectsWithNetworks(newNetwork, liveNetworks); !intersectsLive {
- break
- }
- logrus.Debugf("network %s is being used by a network interface", nextNetwork.String())
- nextNetwork = newNetwork
- }
- return nextNetwork, nil
-}
-
-func allocatorToIPNets(networks []*allocator.Net) []*net.IPNet {
- var nets []*net.IPNet
- for _, network := range networks {
- if len(network.IPAM.Ranges) > 0 {
- // this is the new IPAM range style
- // append each subnet from ipam the rangeset
- for _, allocatorRange := range network.IPAM.Ranges {
- for _, r := range allocatorRange {
- nets = append(nets, newIPNetFromSubnet(r.Subnet))
- }
- }
- } else {
- // looks like the old, deprecated style
- nets = append(nets, newIPNetFromSubnet(network.IPAM.Subnet))
- }
- }
- return nets
-}
-
-func newIPNetFromSubnet(subnet types.IPNet) *net.IPNet {
- n := net.IPNet{
- IP: subnet.IP,
- Mask: subnet.Mask,
- }
- return &n
-}
-
-func networkIntersectsWithNetworks(n *net.IPNet, networklist []*net.IPNet) (bool, *net.IPNet) {
- for _, nw := range networklist {
- if networkIntersect(n, nw) {
- return true, nw
- }
- }
- return false, nil
-}
-
-func networkIntersect(n1, n2 *net.IPNet) bool {
- return n2.Contains(n1.IP) || n1.Contains(n2.IP)
-}
-
-// ValidateUserNetworkIsAvailable returns via an error if a network is available
-// to be used
-func ValidateUserNetworkIsAvailable(config *config.Config, userNet *net.IPNet) error {
- if len(userNet.IP) == 0 || len(userNet.Mask) == 0 {
- return errors.Errorf("network %s's ip or mask cannot be empty", userNet.String())
- }
-
- ones, bit := userNet.Mask.Size()
- if ones == 0 || bit == 0 {
- return errors.Errorf("network %s's mask is invalid", userNet.String())
- }
-
- networks, err := GetNetworksFromFilesystem(config)
- if err != nil {
- return err
- }
- liveNetworks, err := GetLiveNetworks()
- if err != nil {
- return err
- }
- logrus.Debugf("checking if network %s exists in cni networks", userNet.String())
- if intersectsConfig, _ := networkIntersectsWithNetworks(userNet, allocatorToIPNets(networks)); intersectsConfig {
- return errors.Errorf("network %s is already being used by a cni configuration", userNet.String())
- }
- logrus.Debugf("checking if network %s exists in any network interfaces", userNet.String())
- if intersectsLive, _ := networkIntersectsWithNetworks(userNet, liveNetworks); intersectsLive {
- return errors.Errorf("network %s is being used by a network interface", userNet.String())
- }
- return nil
-}
-
-// removeNetwork is removes a cni network without a lock and should only be called
-// when a lock was otherwise acquired.
-func removeNetwork(config *config.Config, name string) error {
- cniPath, err := GetCNIConfigPathByNameOrID(config, name)
- if err != nil {
- return err
- }
- // Before we delete the configuration file, we need to make sure we can read and parse
- // it to get the network interface name so we can remove that too
- interfaceName, err := GetInterfaceNameFromConfig(cniPath)
- if err == nil {
- // Don't try to remove the network interface if we are not root
- if !rootless.IsRootless() {
- liveNetworkNames, err := GetLiveNetworkNames()
- if err != nil {
- return errors.Wrapf(err, "failed to get live network names")
- }
- if util.StringInSlice(interfaceName, liveNetworkNames) {
- if err = RemoveInterface(interfaceName); err != nil {
- // only log the error, it is not fatal
- logrus.Infof("failed to remove network interface %s: %v", interfaceName, err)
- }
- }
- }
- } else if err != ErrNoSuchNetworkInterface {
- // Don't error if we couldn't find the network interface name
- return err
- }
- // Remove the configuration file
- if err := os.Remove(cniPath); err != nil {
- return errors.Wrap(err, "failed to remove network configuration")
- }
- return nil
-}
-
-// RemoveNetwork removes a given network by name. If the network has container associated with it, that
-// must be handled outside the context of this.
-func RemoveNetwork(config *config.Config, name string) error {
- l, err := acquireCNILock(config)
- if err != nil {
- return err
- }
- defer l.releaseCNILock()
- return removeNetwork(config, name)
-}
-
-// InspectNetwork reads a CNI config and returns its configuration
-func InspectNetwork(config *config.Config, name string) (map[string]interface{}, error) {
- b, err := ReadRawCNIConfByNameOrID(config, name)
- if err != nil {
- return nil, err
- }
- rawList := make(map[string]interface{})
- err = json.Unmarshal(b, &rawList)
- return rawList, err
-}
-
-// Exists says whether a given network exists or not; it meant
-// specifically for restful responses so 404s can be used
-func Exists(config *config.Config, name string) (bool, error) {
- _, err := ReadRawCNIConfByNameOrID(config, name)
- if err != nil {
- if errors.Cause(err) == define.ErrNoSuchNetwork {
- return false, nil
- }
- return false, err
- }
- return true, nil
-}
-
-// PruneNetworks removes networks that are not being used and that is not the default
-// network. To keep proper fencing for imports, you must provide the used networks
-// to this function as a map. the key is meaningful in the map, the book is a no-op
-func PruneNetworks(rtc *config.Config, usedNetworks map[string]bool) ([]*entities.NetworkPruneReport, error) {
- var reports []*entities.NetworkPruneReport
- lock, err := acquireCNILock(rtc)
- if err != nil {
- return nil, err
- }
- defer lock.releaseCNILock()
- nets, err := GetNetworkNamesFromFileSystem(rtc)
- if err != nil {
- return nil, err
- }
- for _, n := range nets {
- _, found := usedNetworks[n]
- // Remove is not default network and not found in the used list
- if n != rtc.Network.DefaultNetwork && !found {
- reports = append(reports, &entities.NetworkPruneReport{
- Name: n,
- Error: removeNetwork(rtc, n),
- })
- }
- }
- return reports, nil
-}
-
-// NormalizeName translates a network ID into a name.
-// If the input is a name the name is returned.
-func NormalizeName(config *config.Config, nameOrID string) (string, error) {
- path, err := GetCNIConfigPathByNameOrID(config, nameOrID)
- if err != nil {
- return "", err
- }
- conf, err := libcni.ConfListFromFile(path)
- if err != nil {
- return "", err
- }
- return conf.Name, nil
-}
diff --git a/libpod/network/network_test.go b/libpod/network/network_test.go
deleted file mode 100644
index 1969e792c..000000000
--- a/libpod/network/network_test.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package network
-
-import (
- "net"
- "testing"
-)
-
-func parseCIDR(n string) *net.IPNet {
- _, parsedNet, _ := net.ParseCIDR(n)
- return parsedNet
-}
-
-func Test_networkIntersect(t *testing.T) {
- type args struct {
- n1 *net.IPNet
- n2 *net.IPNet
- }
- tests := []struct {
- name string
- args args
- want bool
- }{
- {"16 and 24 intersects", args{n1: parseCIDR("192.168.0.0/16"), n2: parseCIDR("192.168.1.0/24")}, true},
- {"24 and 25 intersects", args{n1: parseCIDR("192.168.1.0/24"), n2: parseCIDR("192.168.1.0/25")}, true},
- {"Two 24s", args{n1: parseCIDR("192.168.1.0/24"), n2: parseCIDR("192.168.2.0/24")}, false},
- }
- for _, tt := range tests {
- test := tt
- t.Run(tt.name, func(t *testing.T) {
- if got := networkIntersect(test.args.n1, test.args.n2); got != test.want {
- t.Errorf("networkIntersect() = %v, want %v", got, test.want)
- }
- })
- }
-}
diff --git a/libpod/network/subnet.go b/libpod/network/subnet.go
deleted file mode 100644
index 120038e57..000000000
--- a/libpod/network/subnet.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package network
-
-/*
- The code in this was kindly contributed by Dan Williams(dcbw@redhat.com). Many thanks
- for his contributions.
-*/
-
-import (
- "fmt"
- "net"
-)
-
-func incByte(subnet *net.IPNet, idx int, shift uint) error {
- if idx < 0 {
- return fmt.Errorf("no more subnets left")
- }
- if subnet.IP[idx] == 255 {
- subnet.IP[idx] = 0
- return incByte(subnet, idx-1, 0)
- }
- subnet.IP[idx] += 1 << shift
- return nil
-}
-
-// NextSubnet returns subnet incremented by 1
-func NextSubnet(subnet *net.IPNet) (*net.IPNet, error) {
- newSubnet := &net.IPNet{
- IP: subnet.IP,
- Mask: subnet.Mask,
- }
- ones, bits := newSubnet.Mask.Size()
- if ones == 0 {
- return nil, fmt.Errorf("%s has only one subnet", subnet.String())
- }
- zeroes := uint(bits - ones)
- shift := zeroes % 8
- idx := ones/8 - 1
- if idx < 0 {
- idx = 0
- }
- if err := incByte(newSubnet, idx, shift); err != nil {
- return nil, err
- }
- return newSubnet, nil
-}
-
-// LastIPInSubnet gets the last IP in a subnet
-func LastIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer
- // re-parse to ensure clean network address
- _, cidr, err := net.ParseCIDR(addr.String())
- if err != nil {
- return nil, err
- }
-
- ones, bits := cidr.Mask.Size()
- if ones == bits {
- return cidr.IP, nil
- }
- for i := range cidr.IP {
- cidr.IP[i] = cidr.IP[i] | ^cidr.Mask[i]
- }
- return cidr.IP, nil
-}
-
-// FirstIPInSubnet gets the first IP in a subnet
-func FirstIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer
- // re-parse to ensure clean network address
- _, cidr, err := net.ParseCIDR(addr.String())
- if err != nil {
- return nil, err
- }
- ones, bits := cidr.Mask.Size()
- if ones == bits {
- return cidr.IP, nil
- }
- cidr.IP[len(cidr.IP)-1]++
- return cidr.IP, nil
-}
diff --git a/libpod/network/subnet_test.go b/libpod/network/subnet_test.go
deleted file mode 100644
index 55b2443bd..000000000
--- a/libpod/network/subnet_test.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package network
-
-import (
- "net"
- "reflect"
- "testing"
-)
-
-func TestNextSubnet(t *testing.T) {
- type args struct {
- subnet *net.IPNet
- }
- tests := []struct {
- name string
- args args
- want *net.IPNet
- wantErr bool
- }{
- {"class b", args{subnet: parseCIDR("192.168.0.0/16")}, parseCIDR("192.169.0.0/16"), false},
- {"class c", args{subnet: parseCIDR("192.168.1.0/24")}, parseCIDR("192.168.2.0/24"), false},
- }
- for _, tt := range tests {
- test := tt
- t.Run(test.name, func(t *testing.T) {
- got, err := NextSubnet(test.args.subnet)
- if (err != nil) != test.wantErr {
- t.Errorf("NextSubnet() error = %v, wantErr %v", err, test.wantErr)
- return
- }
- if !reflect.DeepEqual(got, test.want) {
- t.Errorf("NextSubnet() got = %v, want %v", got, test.want)
- }
- })
- }
-}
-
-func TestFirstIPInSubnet(t *testing.T) {
- tests := []struct {
- name string
- args *net.IPNet
- want net.IP
- wantErr bool
- }{
- {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.0.1"), false},
- {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.1"), false},
- {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.0.1"), false},
- {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.1"), false},
- {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.129"), false},
- {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.0.0.1"), false},
- {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false},
- {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false},
- }
- for _, tt := range tests {
- test := tt
- t.Run(test.name, func(t *testing.T) {
- got, err := FirstIPInSubnet(test.args)
- if (err != nil) != test.wantErr {
- t.Errorf("FirstIPInSubnet() error = %v, wantErr %v", err, test.wantErr)
- return
- }
- if !got.Equal(test.want) {
- t.Errorf("FirstIPInSubnet() got = %v, want %v", got, test.want)
- }
- })
- }
-}
-
-func TestLastIPInSubnet(t *testing.T) {
- tests := []struct {
- name string
- args *net.IPNet
- want net.IP
- wantErr bool
- }{
- {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.255.255"), false},
- {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.255"), false},
- {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.1.255"), false},
- {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.127"), false},
- {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.191"), false},
- {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.255.255.255"), false},
- {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false},
- {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false},
- }
- for _, tt := range tests {
- test := tt
- t.Run(test.name, func(t *testing.T) {
- got, err := LastIPInSubnet(test.args)
- if (err != nil) != test.wantErr {
- t.Errorf("LastIPInSubnet() error = %v, wantErr %v", err, test.wantErr)
- return
- }
- if !got.Equal(test.want) {
- t.Errorf("LastIPInSubnet() got = %v, want %v", got, test.want)
- }
- })
- }
-}
diff --git a/libpod/network/types/network.go b/libpod/network/types/network.go
index c2c598f46..56bde716e 100644
--- a/libpod/network/types/network.go
+++ b/libpod/network/types/network.go
@@ -36,8 +36,7 @@ type Network struct {
// InterfaceName is the network interface name on the host.
NetworkInterface string `json:"network_interface,omitempty"`
// Created contains the timestamp when this network was created.
- // This is not guaranteed to stay exactly the same.
- Created time.Time
+ Created time.Time `json:"created,omitempty"`
// Subnets to use.
Subnets []Subnet `json:"subnets,omitempty"`
// IPv6Enabled if set to true an ipv6 subnet should be created for this net.
@@ -92,9 +91,11 @@ func (n *IPNet) UnmarshalText(text []byte) error {
}
type Subnet struct {
- // Subnet for this Network.
+ // Subnet for this Network in CIDR form.
+ // swagger:strfmt string
Subnet IPNet `json:"subnet,omitempty"`
// Gateway IP for this Network.
+ // swagger:strfmt string
Gateway net.IP `json:"gateway,omitempty"`
// LeaseRange contains the range where IP are leased. Optional.
LeaseRange *LeaseRange `json:"lease_range,omitempty"`
@@ -103,8 +104,10 @@ type Subnet struct {
// LeaseRange contains the range where IP are leased.
type LeaseRange struct {
// StartIP first IP in the subnet which should be used to assign ips.
+ // swagger:strfmt string
StartIP net.IP `json:"start_ip,omitempty"`
// EndIP last IP in the subnet which should be used to assign ips.
+ // swagger:strfmt string
EndIP net.IP `json:"end_ip,omitempty"`
}
diff --git a/libpod/network/util/filters.go b/libpod/network/util/filters.go
index 48e769196..c3c80b352 100644
--- a/libpod/network/util/filters.go
+++ b/libpod/network/util/filters.go
@@ -28,12 +28,6 @@ func createFilterFuncs(key string, filterValues []string) (types.FilterFunc, err
return util.StringMatchRegexSlice(net.Name, filterValues)
}, nil
- case "label":
- // matches all labels
- return func(net types.Network) bool {
- return util.MatchLabelFilters(filterValues, net.Labels)
- }, nil
-
case "driver":
// matches network driver
return func(net types.Network) bool {
@@ -46,9 +40,39 @@ func createFilterFuncs(key string, filterValues []string) (types.FilterFunc, err
return util.StringMatchRegexSlice(net.ID, filterValues)
}, nil
- // FIXME: What should we do with the old plugin filter
- // TODO: add dangling, dns enabled, internal filter
+ // TODO: add dns enabled, internal filter
+ }
+ return createPruneFilterFuncs(key, filterValues)
+}
+
+func GenerateNetworkPruneFilters(filters map[string][]string) ([]types.FilterFunc, error) {
+ filterFuncs := make([]types.FilterFunc, 0, len(filters))
+ for key, filterValues := range filters {
+ filterFunc, err := createPruneFilterFuncs(key, filterValues)
+ if err != nil {
+ return nil, err
+ }
+ filterFuncs = append(filterFuncs, filterFunc)
+ }
+ return filterFuncs, nil
+}
+
+func createPruneFilterFuncs(key string, filterValues []string) (types.FilterFunc, error) {
+ switch strings.ToLower(key) {
+ case "label":
+ // matches all labels
+ return func(net types.Network) bool {
+ return util.MatchLabelFilters(filterValues, net.Labels)
+ }, nil
+ case "until":
+ until, err := util.ComputeUntilTimestamp(filterValues)
+ if err != nil {
+ return nil, err
+ }
+ return func(net types.Network) bool {
+ return net.Created.Before(until)
+ }, nil
default:
return nil, errors.Errorf("invalid filter %q", key)
}