github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/network/network.go (about)

     1  package network
     2  
     3  import (
     4  	"encoding/json"
     5  	"net"
     6  	"os"
     7  
     8  	"github.com/containernetworking/cni/pkg/types"
     9  	"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
    10  	"github.com/containers/libpod/pkg/util"
    11  	"github.com/pkg/errors"
    12  	"github.com/sirupsen/logrus"
    13  )
    14  
    15  // SupportedNetworkDrivers describes the list of supported drivers
    16  var SupportedNetworkDrivers = []string{"bridge"}
    17  
    18  // IsSupportedDriver checks if the user provided driver is supported
    19  func IsSupportedDriver(driver string) error {
    20  	if util.StringInSlice(driver, SupportedNetworkDrivers) {
    21  		return nil
    22  	}
    23  	return errors.Errorf("driver '%s' is not supported", driver)
    24  }
    25  
    26  // GetLiveNetworks returns a slice of networks representing what the system
    27  // has defined as network interfaces
    28  func GetLiveNetworks() ([]*net.IPNet, error) {
    29  	var nets []*net.IPNet
    30  	addrs, err := net.InterfaceAddrs()
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  	for _, address := range addrs {
    35  		_, n, err := net.ParseCIDR(address.String())
    36  		if err != nil {
    37  			return nil, err
    38  		}
    39  		nets = append(nets, n)
    40  	}
    41  	return nets, nil
    42  }
    43  
    44  // GetLiveNetworkNames returns a list of network interfaces on the system
    45  func GetLiveNetworkNames() ([]string, error) {
    46  	var interfaceNames []string
    47  	liveInterfaces, err := net.Interfaces()
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	for _, i := range liveInterfaces {
    52  		interfaceNames = append(interfaceNames, i.Name)
    53  	}
    54  	return interfaceNames, nil
    55  }
    56  
    57  // GetFreeNetwork looks for a free network according to existing cni configuration
    58  // files and network interfaces.
    59  func GetFreeNetwork() (*net.IPNet, error) {
    60  	networks, err := GetNetworksFromFilesystem()
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	liveNetworks, err := GetLiveNetworks()
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	nextNetwork, err := GetDefaultPodmanNetwork()
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	logrus.Debugf("default network is %s", nextNetwork.String())
    73  	for {
    74  		newNetwork, err := NextSubnet(nextNetwork)
    75  		if err != nil {
    76  			return nil, err
    77  		}
    78  		logrus.Debugf("checking if network %s intersects with other cni networks", nextNetwork.String())
    79  		if intersectsConfig, _ := networkIntersectsWithNetworks(newNetwork, allocatorToIPNets(networks)); intersectsConfig {
    80  			logrus.Debugf("network %s is already being used by a cni configuration", nextNetwork.String())
    81  			nextNetwork = newNetwork
    82  			continue
    83  		}
    84  		logrus.Debugf("checking if network %s intersects with any network interfaces", nextNetwork.String())
    85  		if intersectsLive, _ := networkIntersectsWithNetworks(newNetwork, liveNetworks); !intersectsLive {
    86  			break
    87  		}
    88  		logrus.Debugf("network %s is being used by a network interface", nextNetwork.String())
    89  		nextNetwork = newNetwork
    90  	}
    91  	return nextNetwork, nil
    92  }
    93  
    94  func allocatorToIPNets(networks []*allocator.Net) []*net.IPNet {
    95  	var nets []*net.IPNet
    96  	for _, network := range networks {
    97  		if len(network.IPAM.Ranges) > 0 {
    98  			// this is the new IPAM range style
    99  			// append each subnet from ipam the rangeset
   100  			for _, r := range network.IPAM.Ranges[0] {
   101  				nets = append(nets, newIPNetFromSubnet(r.Subnet))
   102  			}
   103  		} else {
   104  			//	 looks like the old, deprecated style
   105  			nets = append(nets, newIPNetFromSubnet(network.IPAM.Subnet))
   106  		}
   107  	}
   108  	return nets
   109  }
   110  
   111  func newIPNetFromSubnet(subnet types.IPNet) *net.IPNet {
   112  	n := net.IPNet{
   113  		IP:   subnet.IP,
   114  		Mask: subnet.Mask,
   115  	}
   116  	return &n
   117  }
   118  
   119  func networkIntersectsWithNetworks(n *net.IPNet, networklist []*net.IPNet) (bool, *net.IPNet) {
   120  	for _, nw := range networklist {
   121  		if networkIntersect(n, nw) {
   122  			return true, nw
   123  		}
   124  	}
   125  	return false, nil
   126  }
   127  
   128  func networkIntersect(n1, n2 *net.IPNet) bool {
   129  	return n2.Contains(n1.IP) || n1.Contains(n2.IP)
   130  }
   131  
   132  // ValidateUserNetworkIsAvailable returns via an error if a network is available
   133  // to be used
   134  func ValidateUserNetworkIsAvailable(userNet *net.IPNet) error {
   135  	networks, err := GetNetworksFromFilesystem()
   136  	if err != nil {
   137  		return err
   138  	}
   139  	liveNetworks, err := GetLiveNetworks()
   140  	if err != nil {
   141  		return err
   142  	}
   143  	logrus.Debugf("checking if network %s exists in cni networks", userNet.String())
   144  	if intersectsConfig, _ := networkIntersectsWithNetworks(userNet, allocatorToIPNets(networks)); intersectsConfig {
   145  		return errors.Errorf("network %s is already being used by a cni configuration", userNet.String())
   146  	}
   147  	logrus.Debugf("checking if network %s exists in any network interfaces", userNet.String())
   148  	if intersectsLive, _ := networkIntersectsWithNetworks(userNet, liveNetworks); intersectsLive {
   149  		return errors.Errorf("network %s is being used by a network interface", userNet.String())
   150  	}
   151  	return nil
   152  }
   153  
   154  // RemoveNetwork removes a given network by name.  If the network has container associated with it, that
   155  // must be handled outside the context of this.
   156  func RemoveNetwork(name string) error {
   157  	cniPath, err := GetCNIConfigPathByName(name)
   158  	if err != nil {
   159  		return err
   160  	}
   161  	// Before we delete the configuration file, we need to make sure we can read and parse
   162  	// it to get the network interface name so we can remove that too
   163  	interfaceName, err := GetInterfaceNameFromConfig(cniPath)
   164  	if err != nil {
   165  		return errors.Wrapf(err, "failed to find network interface name in %q", cniPath)
   166  	}
   167  	liveNetworkNames, err := GetLiveNetworkNames()
   168  	if err != nil {
   169  		return errors.Wrapf(err, "failed to get live network names")
   170  	}
   171  	if util.StringInSlice(interfaceName, liveNetworkNames) {
   172  		if err := RemoveInterface(interfaceName); err != nil {
   173  			return errors.Wrapf(err, "failed to delete the network interface %q", interfaceName)
   174  		}
   175  	}
   176  	// Remove the configuration file
   177  	if err := os.Remove(cniPath); err != nil {
   178  		return errors.Wrapf(err, "failed to remove network configuration file %q", cniPath)
   179  	}
   180  	return nil
   181  }
   182  
   183  // InspectNetwork reads a CNI config and returns its configuration
   184  func InspectNetwork(name string) (map[string]interface{}, error) {
   185  	b, err := ReadRawCNIConfByName(name)
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  	rawList := make(map[string]interface{})
   190  	err = json.Unmarshal(b, &rawList)
   191  	return rawList, err
   192  }