146 lines
4.4 KiB
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")
|
|
}
|
|
}
|
|
}
|