github.com/AbhinandanKurakure/podman/v3@v3.4.10/libpod/network/create.go (about)

     1  package network
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strconv"
    10  
    11  	"github.com/containernetworking/cni/pkg/version"
    12  	"github.com/containers/common/pkg/config"
    13  	"github.com/containers/podman/v3/pkg/domain/entities"
    14  	"github.com/containers/podman/v3/pkg/util"
    15  	"github.com/pkg/errors"
    16  	"github.com/sirupsen/logrus"
    17  )
    18  
    19  // Create the CNI network
    20  func Create(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (*entities.NetworkCreateReport, error) {
    21  	var fileName string
    22  	if err := isSupportedDriver(options.Driver); err != nil {
    23  		return nil, err
    24  	}
    25  	// Acquire a lock for CNI
    26  	l, err := acquireCNILock(runtimeConfig)
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  	defer l.releaseCNILock()
    31  	if len(options.MacVLAN) > 0 || options.Driver == MacVLANNetworkDriver {
    32  		fileName, err = createMacVLAN(name, options, runtimeConfig)
    33  	} else {
    34  		fileName, err = createBridge(name, options, runtimeConfig)
    35  	}
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  	return &entities.NetworkCreateReport{Filename: fileName}, nil
    40  }
    41  
    42  // validateBridgeOptions validate the bridge networking options
    43  func validateBridgeOptions(options entities.NetworkCreateOptions) error {
    44  	subnet := &options.Subnet
    45  	ipRange := &options.Range
    46  	gateway := options.Gateway
    47  	// if IPv6 is set an IPv6 subnet MUST be specified
    48  	if options.IPv6 && ((subnet.IP == nil) || (subnet.IP != nil && !IsIPv6(subnet.IP))) {
    49  		return errors.Errorf("ipv6 option requires an IPv6 --subnet to be provided")
    50  	}
    51  	// range and gateway depend on subnet
    52  	if subnet.IP == nil && (ipRange.IP != nil || gateway != nil) {
    53  		return errors.Errorf("every ip-range or gateway must have a corresponding subnet")
    54  	}
    55  
    56  	// if a range is given, we need to ensure it is "in" the network range.
    57  	if ipRange.IP != nil {
    58  		firstIP, err := FirstIPInSubnet(ipRange)
    59  		if err != nil {
    60  			return errors.Wrapf(err, "failed to get first IP address from ip-range")
    61  		}
    62  		lastIP, err := LastIPInSubnet(ipRange)
    63  		if err != nil {
    64  			return errors.Wrapf(err, "failed to get last IP address from ip-range")
    65  		}
    66  		if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) {
    67  			return errors.Errorf("the ip range %s does not fall within the subnet range %s", ipRange.String(), subnet.String())
    68  		}
    69  	}
    70  
    71  	// if network is provided and if gateway is provided, make sure it is "in" network
    72  	if gateway != nil && !subnet.Contains(gateway) {
    73  		return errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String())
    74  	}
    75  
    76  	return nil
    77  }
    78  
    79  // parseMTU parses the mtu option
    80  func parseMTU(mtu string) (int, error) {
    81  	if mtu == "" {
    82  		return 0, nil // default
    83  	}
    84  	m, err := strconv.Atoi(mtu)
    85  	if err != nil {
    86  		return 0, err
    87  	}
    88  	if m < 0 {
    89  		return 0, errors.Errorf("the value %d for mtu is less than zero", m)
    90  	}
    91  	return m, nil
    92  }
    93  
    94  // parseVlan parses the vlan option
    95  func parseVlan(vlan string) (int, error) {
    96  	if vlan == "" {
    97  		return 0, nil // default
    98  	}
    99  	return strconv.Atoi(vlan)
   100  }
   101  
   102  // createBridge creates a CNI network
   103  func createBridge(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) {
   104  	var (
   105  		ipamRanges [][]IPAMLocalHostRangeConf
   106  		err        error
   107  		routes     []IPAMRoute
   108  	)
   109  	isGateway := true
   110  	ipMasq := true
   111  
   112  	// validate options
   113  	if err := validateBridgeOptions(options); err != nil {
   114  		return "", err
   115  	}
   116  
   117  	// For compatibility with the docker implementation:
   118  	// 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
   119  	// if IPv6 is not specified the subnet may be specified and can be either IPv4 or IPv6 (podman, unlike docker, allows IPv6 only networks)
   120  	// If not subnet is specified an IPv4 subnet will be allocated
   121  	subnet := &options.Subnet
   122  	ipRange := &options.Range
   123  	gateway := options.Gateway
   124  	if subnet.IP != nil {
   125  		// if network is provided, does it conflict with existing CNI or live networks
   126  		err = ValidateUserNetworkIsAvailable(runtimeConfig, subnet)
   127  		if err != nil {
   128  			return "", err
   129  		}
   130  		// obtain CNI subnet default route
   131  		defaultRoute, err := NewIPAMDefaultRoute(IsIPv6(subnet.IP))
   132  		if err != nil {
   133  			return "", err
   134  		}
   135  		routes = append(routes, defaultRoute)
   136  		// obtain CNI range
   137  		ipamRange, err := NewIPAMLocalHostRange(subnet, ipRange, gateway)
   138  		if err != nil {
   139  			return "", err
   140  		}
   141  		ipamRanges = append(ipamRanges, ipamRange)
   142  	}
   143  	// if no network is provided or IPv6 flag used, figure out the IPv4 network
   144  	if options.IPv6 || len(routes) == 0 {
   145  		subnetV4, err := GetFreeNetwork(runtimeConfig)
   146  		if err != nil {
   147  			return "", err
   148  		}
   149  		// obtain IPv4 default route
   150  		defaultRoute, err := NewIPAMDefaultRoute(false)
   151  		if err != nil {
   152  			return "", err
   153  		}
   154  		routes = append(routes, defaultRoute)
   155  		// the CNI bridge plugin does not need to set
   156  		// the range or gateway options explicitly
   157  		ipamRange, err := NewIPAMLocalHostRange(subnetV4, nil, nil)
   158  		if err != nil {
   159  			return "", err
   160  		}
   161  		ipamRanges = append(ipamRanges, ipamRange)
   162  	}
   163  
   164  	// create CNI config
   165  	ipamConfig, err := NewIPAMHostLocalConf(routes, ipamRanges)
   166  	if err != nil {
   167  		return "", err
   168  	}
   169  
   170  	if options.Internal {
   171  		isGateway = false
   172  		ipMasq = false
   173  	}
   174  
   175  	var mtu int
   176  	var vlan int
   177  	for k, v := range options.Options {
   178  		var err error
   179  		switch k {
   180  		case "mtu":
   181  			mtu, err = parseMTU(v)
   182  			if err != nil {
   183  				return "", err
   184  			}
   185  
   186  		case "vlan":
   187  			vlan, err = parseVlan(v)
   188  			if err != nil {
   189  				return "", err
   190  			}
   191  
   192  		default:
   193  			return "", errors.Errorf("unsupported option %s", k)
   194  		}
   195  	}
   196  
   197  	// obtain host bridge name
   198  	bridgeDeviceName, err := GetFreeDeviceName(runtimeConfig)
   199  	if err != nil {
   200  		return "", err
   201  	}
   202  
   203  	if len(name) > 0 {
   204  		netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig)
   205  		if err != nil {
   206  			return "", err
   207  		}
   208  		if util.StringInSlice(name, netNames) {
   209  			return "", errors.Errorf("the network name %s is already used", name)
   210  		}
   211  	} else {
   212  		// If no name is given, we give the name of the bridge device
   213  		name = bridgeDeviceName
   214  	}
   215  
   216  	// create CNI plugin configuration
   217  	ncList := NewNcList(name, version.Current(), options.Labels)
   218  	var plugins []CNIPlugins
   219  	// TODO need to iron out the role of isDefaultGW and IPMasq
   220  	bridge := NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, mtu, vlan, ipamConfig)
   221  	plugins = append(plugins, bridge)
   222  	plugins = append(plugins, NewPortMapPlugin())
   223  	plugins = append(plugins, NewFirewallPlugin())
   224  	plugins = append(plugins, NewTuningPlugin())
   225  	// if we find the dnsname plugin we add configuration for it
   226  	if HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) && !options.DisableDNS {
   227  		if options.Internal {
   228  			logrus.Warnf("dnsname and --internal networks are incompatible.  dnsname plugin not configured for network %s", name)
   229  		} else {
   230  			// Note: in the future we might like to allow for dynamic domain names
   231  			plugins = append(plugins, NewDNSNamePlugin(DefaultPodmanDomainName))
   232  		}
   233  	}
   234  	// Add the podman-machine CNI plugin if we are in a machine
   235  	if runtimeConfig.MachineEnabled() { // check if we are in a machine vm
   236  		plugins = append(plugins, NewPodmanMachinePlugin())
   237  	}
   238  	ncList["plugins"] = plugins
   239  	b, err := json.MarshalIndent(ncList, "", "   ")
   240  	if err != nil {
   241  		return "", err
   242  	}
   243  	if err := os.MkdirAll(GetCNIConfDir(runtimeConfig), 0755); err != nil {
   244  		return "", err
   245  	}
   246  	cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name))
   247  	err = ioutil.WriteFile(cniPathName, b, 0644)
   248  	return cniPathName, err
   249  }
   250  
   251  func createMacVLAN(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) {
   252  	var (
   253  		mtu     int
   254  		plugins []CNIPlugins
   255  	)
   256  	liveNetNames, err := GetLiveNetworkNames()
   257  	if err != nil {
   258  		return "", err
   259  	}
   260  
   261  	// The parent can be defined with --macvlan or as an option (-o parent:device)
   262  	parentNetworkDevice := options.MacVLAN
   263  	if len(parentNetworkDevice) < 1 {
   264  		if parent, ok := options.Options["parent"]; ok {
   265  			parentNetworkDevice = parent
   266  		}
   267  	}
   268  
   269  	// Make sure the host-device exists if provided
   270  	if len(parentNetworkDevice) > 0 && !util.StringInSlice(parentNetworkDevice, liveNetNames) {
   271  		return "", errors.Errorf("failed to find network interface %q", parentNetworkDevice)
   272  	}
   273  	if len(name) > 0 {
   274  		netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig)
   275  		if err != nil {
   276  			return "", err
   277  		}
   278  		if util.StringInSlice(name, netNames) {
   279  			return "", errors.Errorf("the network name %s is already used", name)
   280  		}
   281  	} else {
   282  		name, err = GetFreeDeviceName(runtimeConfig)
   283  		if err != nil {
   284  			return "", err
   285  		}
   286  	}
   287  	ncList := NewNcList(name, version.Current(), options.Labels)
   288  	if val, ok := options.Options["mtu"]; ok {
   289  		intVal, err := strconv.Atoi(val)
   290  		if err != nil {
   291  			return "", err
   292  		}
   293  		if intVal > 0 {
   294  			mtu = intVal
   295  		}
   296  	}
   297  	macvlan, err := NewMacVLANPlugin(parentNetworkDevice, options.Gateway, &options.Range, &options.Subnet, mtu)
   298  	if err != nil {
   299  		return "", err
   300  	}
   301  	plugins = append(plugins, macvlan)
   302  	ncList["plugins"] = plugins
   303  	b, err := json.MarshalIndent(ncList, "", "   ")
   304  	if err != nil {
   305  		return "", err
   306  	}
   307  	cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name))
   308  	err = ioutil.WriteFile(cniPathName, b, 0644)
   309  	return cniPathName, err
   310  }