summaryrefslogtreecommitdiff
path: root/libpod/network/create.go
diff options
context:
space:
mode:
authorAntonio Ojea <aojea@redhat.com>2020-10-25 18:28:44 +0100
committerAntonio Ojea <aojea@redhat.com>2020-11-10 08:34:52 +0100
commite7a72d72fd598b0de3c1049c91cd788440c08f2d (patch)
treeef04f217f7ea7f6a2aeac64ab689c629a8d7eaa9 /libpod/network/create.go
parenta64c6dc90a6d315bc4cccae9fbd13788daa23fb7 (diff)
downloadpodman-e7a72d72fd598b0de3c1049c91cd788440c08f2d.tar.gz
podman-e7a72d72fd598b0de3c1049c91cd788440c08f2d.tar.bz2
podman-e7a72d72fd598b0de3c1049c91cd788440c08f2d.zip
enable ipv6 network configuration options
enable the ipv6 flag in podman network to be able to create dual-stack networks for containers. This is required to be compatible with docker, where --ipv6 really means dual stack. podman, unlike docker, support IPv6 only containers since 07e3f1bba9674c0cb93a0fa260930bfebbf75728. Signed-off-by: Antonio Ojea <aojea@redhat.com>
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)