summaryrefslogtreecommitdiff
path: root/libpod/network/create.go
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/network/create.go')
-rw-r--r--libpod/network/create.go172
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
}