File: util.go

package info (click to toggle)
golang-github-containers-common 0.64.1%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 5,932 kB
  • sloc: makefile: 132; sh: 111
file content (145 lines) | stat: -rw-r--r-- 4,455 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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")
		}
	}
}