286 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			286 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
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 _, r := range network.IPAM.Ranges[0] {
 | 
						|
				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 {
 | 
						|
					return errors.Wrapf(err, "failed to delete the network interface %q", interfaceName)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} 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
 | 
						|
}
 |