diff options
Diffstat (limited to 'libpod/network/create.go')
-rw-r--r-- | libpod/network/create.go | 172 |
1 files changed, 106 insertions, 66 deletions
diff --git a/libpod/network/create.go b/libpod/network/create.go index bf11631bf..7e4fc574a 100644 --- a/libpod/network/create.go +++ b/libpod/network/create.go @@ -8,32 +8,29 @@ import ( "path/filepath" "github.com/containernetworking/cni/pkg/version" - "github.com/containers/podman/v2/libpod" + "github.com/containers/common/pkg/config" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/rootless" "github.com/containers/podman/v2/pkg/util" "github.com/pkg/errors" ) -func Create(name string, options entities.NetworkCreateOptions, r *libpod.Runtime) (*entities.NetworkCreateReport, error) { +// 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 } - config, err := r.GetConfig() - if err != nil { - return nil, err - } // Acquire a lock for CNI - l, err := acquireCNILock(filepath.Join(config.Engine.TmpDir, LockFileName)) + l, err := acquireCNILock(filepath.Join(runtimeConfig.Engine.TmpDir, LockFileName)) if err != nil { return nil, err } defer l.releaseCNILock() if len(options.MacVLAN) > 0 { - fileName, err = createMacVLAN(r, name, options) + fileName, err = createMacVLAN(name, options, runtimeConfig) } else { - fileName, err = createBridge(r, name, options) + fileName, err = createBridge(name, options, runtimeConfig) } if err != nil { return nil, err @@ -41,60 +38,118 @@ func Create(name string, options entities.NetworkCreateOptions, r *libpod.Runtim 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 + +} + // createBridge creates a CNI network -func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) { +func createBridge(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) { + var ( + ipamRanges [][]IPAMLocalHostRangeConf + err error + routes []IPAMRoute + ) isGateway := true ipMasq := true - subnet := &options.Subnet - ipRange := options.Range - runtimeConfig, err := r.GetConfig() - if err != nil { + + // validate options + if err := validateBridgeOptions(options); err != nil { return "", err } - // if range is provided, make sure it is "in" network + + // 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) - } else { - // if no network is provided, figure out network - subnet, err = GetFreeNetwork(runtimeConfig) - } - if err != nil { - return "", err - } - gateway := options.Gateway - if gateway == nil { - // if no gateway is provided, provide it as first ip of network - gateway = CalcGatewayIP(subnet) - } - // if network is provided and if gateway is provided, make sure it is "in" network - if options.Subnet.IP != nil && options.Gateway != nil { - if !subnet.Contains(gateway) { - return "", errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String()) + if err != nil { + return "", err } - } - if options.Internal { - isGateway = false - ipMasq = false - } - - // if a range is given, we need to ensure it is "in" the network range. - if options.Range.IP != nil { - if options.Subnet.IP == nil { - return "", errors.New("you must define a subnet range to define an ip-range") + // 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 } - firstIP, err := FirstIPInSubnet(&options.Range) + 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 } - lastIP, err := LastIPInSubnet(&options.Range) + // obtain IPv4 default route + defaultRoute, err := NewIPAMDefaultRoute(false) if err != nil { return "", err } - if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) { - return "", errors.Errorf("the ip range %s does not fall within the subnet range %s", options.Range.String(), subnet.String()) + 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 + } + + // obtain host bridge name bridgeDeviceName, err := GetFreeDeviceName(runtimeConfig) if err != nil { return "", err @@ -113,25 +168,15 @@ func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreate name = bridgeDeviceName } + // create CNI plugin configuration ncList := NewNcList(name, version.Current()) var plugins []CNIPlugins - var routes []IPAMRoute - - defaultRoute, err := NewIPAMDefaultRoute(IsIPv6(subnet.IP)) - if err != nil { - return "", err - } - routes = append(routes, defaultRoute) - ipamConfig, err := NewIPAMHostLocalConf(subnet, routes, ipRange, gateway) - if err != nil { - return "", err - } - // TODO need to iron out the role of isDefaultGW and IPMasq bridge := NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, ipamConfig) plugins = append(plugins, bridge) plugins = append(plugins, NewPortMapPlugin()) plugins = append(plugins, NewFirewallPlugin()) + plugins = append(plugins, NewTuningPlugin()) // if we find the dnsname plugin or are rootless, we add configuration for it // the rootless-cni-infra container has the dnsname plugin always installed if (HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) || rootless.IsRootless()) && !options.DisableDNS { @@ -151,7 +196,7 @@ func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreate return cniPathName, err } -func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) { +func createMacVLAN(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) { var ( plugins []CNIPlugins ) @@ -160,17 +205,12 @@ func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreat return "", err } - config, err := r.GetConfig() - if err != nil { - return "", err - } - // Make sure the host-device exists if !util.StringInSlice(options.MacVLAN, liveNetNames) { return "", errors.Errorf("failed to find network interface %q", options.MacVLAN) } if len(name) > 0 { - netNames, err := GetNetworkNamesFromFileSystem(config) + netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig) if err != nil { return "", err } @@ -178,7 +218,7 @@ func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreat return "", errors.Errorf("the network name %s is already used", name) } } else { - name, err = GetFreeDeviceName(config) + name, err = GetFreeDeviceName(runtimeConfig) if err != nil { return "", err } @@ -191,7 +231,7 @@ func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreat if err != nil { return "", err } - cniPathName := filepath.Join(GetCNIConfDir(config), fmt.Sprintf("%s.conflist", name)) + cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name)) err = ioutil.WriteFile(cniPathName, b, 0644) return cniPathName, err } |