automation-tests/common/libnetwork/internal/util/util.go

146 lines
4.4 KiB
Go

package util
import (
"errors"
"fmt"
"net"
"slices"
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/config"
"github.com/sirupsen/logrus"
)
// GetBridgeInterfaceNames returns all bridge interface names
// already used by network configs
func GetBridgeInterfaceNames(n NetUtil) []string {
names := make([]string, 0, n.Len())
n.ForEach(func(net types.Network) {
if net.Driver == types.BridgeNetworkDriver {
names = append(names, net.NetworkInterface)
}
})
return names
}
// GetUsedNetworkNames returns all network names already used
// by network configs
func GetUsedNetworkNames(n NetUtil) []string {
names := make([]string, 0, n.Len())
n.ForEach(func(net types.Network) {
names = append(names, net.Name)
})
return names
}
// GetFreeDeviceName returns a free device name which can
// be used for new configs as name and bridge interface name.
// The base name is suffixed by a number
func GetFreeDeviceName(n NetUtil) (string, error) {
bridgeNames := GetBridgeInterfaceNames(n)
netNames := GetUsedNetworkNames(n)
liveInterfaces, err := GetLiveNetworkNames()
if err != nil {
return "", nil
}
names := make([]string, 0, len(bridgeNames)+len(netNames)+len(liveInterfaces))
names = append(names, bridgeNames...)
names = append(names, netNames...)
names = append(names, liveInterfaces...)
// FIXME: Is a limit fine?
// Start by 1, 0 is reserved for the default network
for i := 1; i < 1000000; i++ {
deviceName := fmt.Sprintf("%s%d", n.DefaultInterfaceName(), i)
if !slices.Contains(names, deviceName) {
logrus.Debugf("found free device name %s", deviceName)
return deviceName, nil
}
}
return "", errors.New("could not find free device name, to many iterations")
}
// GetUsedSubnets returns a list of all used subnets by network
// configs and interfaces on the host.
func GetUsedSubnets(n NetUtil) ([]*net.IPNet, error) {
// first, load all used subnets from network configs
subnets := make([]*net.IPNet, 0, n.Len())
n.ForEach(func(n types.Network) {
for i := range n.Subnets {
subnets = append(subnets, &n.Subnets[i].Subnet.IPNet)
}
})
// second, load networks from the current system
liveSubnets, err := getLiveNetworkSubnets()
if err != nil {
return nil, err
}
return append(subnets, liveSubnets...), nil
}
// GetFreeIPv4NetworkSubnet returns a unused ipv4 subnet
func GetFreeIPv4NetworkSubnet(usedNetworks []*net.IPNet, subnetPools []config.SubnetPool) (*types.Subnet, error) {
var err error
for _, pool := range subnetPools {
// make sure to copy the netip to prevent overwriting the subnet pool
netIP := make(net.IP, net.IPv4len)
copy(netIP, pool.Base.IP)
network := &net.IPNet{
IP: netIP,
Mask: net.CIDRMask(pool.Size, 32),
}
for pool.Base.Contains(network.IP) {
if !NetworkIntersectsWithNetworks(network, usedNetworks) {
logrus.Debugf("found free ipv4 network subnet %s", network.String())
return &types.Subnet{
Subnet: types.IPNet{IPNet: *network},
}, nil
}
network, err = NextSubnet(network)
if err != nil {
// when error go to next pool, we return the error only when all pools are done
break
}
}
}
if err != nil {
return nil, err
}
return nil, errors.New("could not find free subnet from subnet pools")
}
// GetFreeIPv6NetworkSubnet returns a unused ipv6 subnet
func GetFreeIPv6NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) {
// FIXME: Is 10000 fine as limit? We should prevent an endless loop.
for range 10000 {
// RFC4193: Choose the ipv6 subnet random and NOT sequentially.
network, err := getRandomIPv6Subnet()
if err != nil {
return nil, err
}
if intersectsConfig := NetworkIntersectsWithNetworks(&network, usedNetworks); !intersectsConfig {
logrus.Debugf("found free ipv6 network subnet %s", network.String())
return &types.Subnet{
Subnet: types.IPNet{IPNet: network},
}, nil
}
}
return nil, errors.New("failed to get random ipv6 subnet")
}
// Map docker driver network options to podman network options
func MapDockerBridgeDriverOptions(n *types.Network) {
// validate the given options
for key, value := range n.Options {
switch key {
case "com.docker.network.driver.mtu":
n.Options[types.MTUOption] = value
delete(n.Options, "com.docker.network.driver.mtu")
case "com.docker.network.bridge.name":
n.NetworkInterface = value
delete(n.Options, "com.docker.network.bridge.name")
}
}
}