Skip to content

Commit

Permalink
Add "auto" option to ipv4-subnet and ipv6-subnet
Browse files Browse the repository at this point in the history
If we do not care about the assigned IPv4/IPv6 subnet for the container
network, we let the container runtime allocate one for us. This
guarantees to avoid a collision of the hardcoded range (172.20.20.0/24
and 2001:172:20:20::/64) with any existing container networks.

This just works for the podman runtime. For the docker runtime we
compute the IPv6 subnet manually because it can only pick addresses from
the configured (global) subnet.
  • Loading branch information
mzagozen committed Mar 28, 2024
1 parent 4926307 commit 9f46409
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 5 deletions.
17 changes: 17 additions & 0 deletions clab/config/utils.go
@@ -1,6 +1,8 @@
package config

import (
"crypto/rand"
"encoding/hex"
"fmt"
"net/netip"
"path/filepath"
Expand Down Expand Up @@ -271,6 +273,21 @@ func ipFarEnd(in netip.Prefix) netip.Prefix {
return netip.PrefixFrom(n, in.Bits())
}

// GenerateIPv6ULASubnet creates a random /64 IPv6 subnet from the ULA (Unique Local Address) range
func GenerateIPv6ULASubnet() (string, error) {
ula := "fd00:"
for i := 0; i < 3; i++ {
// Generate a random 16-bit hex field
bytes := make([]byte, 2)
if _, err := rand.Read(bytes); err != nil {
return "", err
}
ula += hex.EncodeToString(bytes) + ":"
}
ula += ":/64"
return ula, nil
}

// GetTemplateNamesInDirs returns a list of template file names found in a list of dir `paths`
// without traversing nested dirs
// template names are following the pattern <some-name>__<role/kind>.tmpl.
Expand Down
16 changes: 13 additions & 3 deletions runtime/docker/docker.go
Expand Up @@ -206,7 +206,7 @@ func (d *DockerRuntime) createMgmtBridge(nctx context.Context, bridgeName string
log.Debugf("bridge %q has ipv4 addr of %q and ipv6 addr of %q", d.mgmt.Bridge, v4gw, v6gw)
}

if d.mgmt.IPv4Subnet != "" {
if d.mgmt.IPv4Subnet != "" && d.mgmt.IPv4Subnet != "auto" {
if d.mgmt.IPv4Gw != "" {
v4gw = d.mgmt.IPv4Gw
}
Expand All @@ -220,12 +220,22 @@ func (d *DockerRuntime) createMgmtBridge(nctx context.Context, bridgeName string
ipamConfig = append(ipamConfig, ipamCfg)
}

if d.mgmt.IPv6Subnet != "" {
var ipv6_subnet string
if d.mgmt.IPv6Subnet == "auto" {
ipv6_subnet, err = utils.GenerateIPv6ULASubnet()
if err != nil {
return "", err
}
} else {
ipv6_subnet = d.mgmt.IPv6Subnet
}

if ipv6_subnet != "" {
if d.mgmt.IPv6Gw != "" {
v6gw = d.mgmt.IPv6Gw
}
ipamCfg := network.IPAMConfig{
Subnet: d.mgmt.IPv6Subnet,
Subnet: ipv6_subnet,
Gateway: v6gw,
}
if d.mgmt.IPv6Range != "" {
Expand Down
4 changes: 2 additions & 2 deletions runtime/podman/util.go
Expand Up @@ -421,7 +421,7 @@ func (r *PodmanRuntime) netOpts(_ context.Context) (netTypes.Network, error) {
)
// parse mgmt subnets
// check if v4 is defined
if r.mgmt.IPv4Subnet != "" {
if r.mgmt.IPv4Subnet != "" && r.mgmt.IPv4Subnet != "auto" {
v4subnet.Subnet, err = netTypes.ParseCIDR(r.mgmt.IPv4Subnet)
if err != nil {
return netTypes.Network{}, err
Expand All @@ -434,7 +434,7 @@ func (r *PodmanRuntime) netOpts(_ context.Context) (netTypes.Network, error) {
log.Debugf("Added v4 subnet info to the net definion: \n%v, \n%v\n", subnets, v4subnet)
}
// check if v6 is defined
if r.mgmt.IPv6Subnet != "" {
if r.mgmt.IPv6Subnet != "" && r.mgmt.IPv6Subnet != "auto" {
v6subnet.Subnet, err = netTypes.ParseCIDR(r.mgmt.IPv6Subnet)
if err != nil {
return netTypes.Network{}, err
Expand Down
21 changes: 21 additions & 0 deletions utils/ipv6_ula.go
@@ -0,0 +1,21 @@
package utils

import (
"crypto/rand"
"encoding/hex"
)

// GenerateIPv6ULASubnet creates a random /64 IPv6 subnet from the ULA (Unique Local Address) range
func GenerateIPv6ULASubnet() (string, error) {
ula := "fd00:"
for i := 0; i < 3; i++ {
// Generate a random 16-bit hex field
bytes := make([]byte, 2)
if _, err := rand.Read(bytes); err != nil {
return "", err
}
ula += hex.EncodeToString(bytes) + ":"
}
ula += ":/64"
return ula, nil
}

0 comments on commit 9f46409

Please sign in to comment.