summaryrefslogtreecommitdiff
path: root/libpod/network/create.go
diff options
context:
space:
mode:
authorOpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com>2020-11-10 13:50:43 +0000
committerGitHub <noreply@github.com>2020-11-10 13:50:43 +0000
commit20b26b521079234319b362c5a840e30d85f4f9b3 (patch)
tree629cd28989968869dad0f48dec0b848987aba39e /libpod/network/create.go
parentda95fb4226c82694af19610578d9c14a6aa964f2 (diff)
parentaabf28a16825aec643206d5e3084940ea48b9c99 (diff)
downloadpodman-20b26b521079234319b362c5a840e30d85f4f9b3.tar.gz
podman-20b26b521079234319b362c5a840e30d85f4f9b3.tar.bz2
podman-20b26b521079234319b362c5a840e30d85f4f9b3.zip
Merge pull request #8143 from aojea/dual
enable ipv6 networks
Diffstat (limited to 'libpod/network/create.go')
-rw-r--r--libpod/network/create.go138
1 files changed, 94 insertions, 44 deletions
diff --git a/libpod/network/create.go b/libpod/network/create.go
index bf11631bf..c11904ecf 100644
--- a/libpod/network/create.go
+++ b/libpod/network/create.go
@@ -15,6 +15,7 @@ import (
"github.com/pkg/errors"
)
+// Create the CNI network
func Create(name string, options entities.NetworkCreateOptions, r *libpod.Runtime) (*entities.NetworkCreateReport, error) {
var fileName string
if err := isSupportedDriver(options.Driver); err != nil {
@@ -41,60 +42,120 @@ 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) {
isGateway := true
ipMasq := true
- subnet := &options.Subnet
- ipRange := options.Range
runtimeConfig, err := r.GetConfig()
if err != nil {
return "", err
}
- // if range is provided, make sure it is "in" network
- 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)
- }
+
+ // validate options
+ err = validateBridgeOptions(options)
if err != nil {
return "", err
}
+
+ // 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 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())
+ var ipamRanges [][]IPAMLocalHostRangeConf
+ var routes []IPAMRoute
+ if subnet.IP != nil {
+ // if network is provided, does it conflict with existing CNI or live networks
+ err = ValidateUserNetworkIsAvailable(runtimeConfig, subnet)
+ 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
}
- firstIP, err := FirstIPInSubnet(&options.Range)
+ routes = append(routes, defaultRoute)
+ // obtain CNI range
+ ipamRange, err := NewIPAMLocalHostRange(subnet, ipRange, gateway)
if err != nil {
return "", err
}
- lastIP, err := LastIPInSubnet(&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
}
- 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())
+ // obtain IPv4 default route
+ defaultRoute, err := NewIPAMDefaultRoute(false)
+ if err != nil {
+ return "", err
}
+ 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,20 +174,9 @@ 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)