summaryrefslogtreecommitdiff
path: root/libpod/network/cni
diff options
context:
space:
mode:
authorPaul Holzinger <pholzing@redhat.com>2021-10-13 21:52:55 +0200
committerPaul Holzinger <pholzing@redhat.com>2021-11-11 15:54:02 +0100
commit12c62b92ff2f63cb34dcb9c0555b96983e6aad94 (patch)
tree01e489ea273813b8ae0bd7302c9f91bda5c2f23a /libpod/network/cni
parent8fd31c674b02b800267b2a759e2406902fdb2723 (diff)
downloadpodman-12c62b92ff2f63cb34dcb9c0555b96983e6aad94.tar.gz
podman-12c62b92ff2f63cb34dcb9c0555b96983e6aad94.tar.bz2
podman-12c62b92ff2f63cb34dcb9c0555b96983e6aad94.zip
Make networking code reusable
To prevent code duplication when creating new network backends move reusable code into a separate internal package. This allows all network backends to use the same code as long as they implement the new NetUtil interface. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
Diffstat (limited to 'libpod/network/cni')
-rw-r--r--libpod/network/cni/config.go152
-rw-r--r--libpod/network/cni/network.go116
-rw-r--r--libpod/network/cni/run.go39
-rw-r--r--libpod/network/cni/run_test.go2
4 files changed, 28 insertions, 281 deletions
diff --git a/libpod/network/cni/config.go b/libpod/network/cni/config.go
index 3df155637..a63574f25 100644
--- a/libpod/network/cni/config.go
+++ b/libpod/network/cni/config.go
@@ -7,6 +7,7 @@ import (
"os"
"github.com/containers/podman/v3/libpod/define"
+ internalutil "github.com/containers/podman/v3/libpod/network/internal/util"
"github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/libpod/network/util"
pkgutil "github.com/containers/podman/v3/pkg/util"
@@ -41,38 +42,9 @@ func (n *cniNetwork) networkCreate(newNetwork types.Network, defaultNet bool) (*
newNetwork.Driver = types.DefaultNetworkDriver
}
- // FIXME: Should we use a different type for network create without the ID field?
- // the caller is not allowed to set a specific ID
- if newNetwork.ID != "" {
- return nil, errors.Wrap(define.ErrInvalidArg, "ID can not be set for network create")
- }
-
- if newNetwork.Labels == nil {
- newNetwork.Labels = map[string]string{}
- }
- if newNetwork.Options == nil {
- newNetwork.Options = map[string]string{}
- }
- if newNetwork.IPAMOptions == nil {
- newNetwork.IPAMOptions = map[string]string{}
- }
-
- var name string
- var err error
- // validate the name when given
- if newNetwork.Name != "" {
- if !define.NameRegex.MatchString(newNetwork.Name) {
- return nil, errors.Wrapf(define.RegexError, "network name %s invalid", newNetwork.Name)
- }
- if _, ok := n.networks[newNetwork.Name]; ok {
- return nil, errors.Wrapf(define.ErrNetworkExists, "network name %s already used", newNetwork.Name)
- }
- } else {
- name, err = n.getFreeDeviceName()
- if err != nil {
- return nil, err
- }
- newNetwork.Name = name
+ err := internalutil.CommonNetworkCreate(n, &newNetwork)
+ if err != nil {
+ return nil, err
}
// Only get the used networks for validation if we do not create the default network.
@@ -84,7 +56,7 @@ func (n *cniNetwork) networkCreate(newNetwork types.Network, defaultNet bool) (*
// fail because it thinks the network is used on the host.
var usedNetworks []*net.IPNet
if !defaultNet {
- usedNetworks, err = n.getUsedSubnets()
+ usedNetworks, err = internalutil.GetUsedSubnets(n)
if err != nil {
return nil, err
}
@@ -92,11 +64,7 @@ func (n *cniNetwork) networkCreate(newNetwork types.Network, defaultNet bool) (*
switch newNetwork.Driver {
case types.BridgeNetworkDriver:
- // if the name was created with getFreeDeviceName set the interface to it as well
- if name != "" && newNetwork.NetworkInterface == "" {
- newNetwork.NetworkInterface = name
- }
- err = n.createBridge(&newNetwork, usedNetworks)
+ err = internalutil.CreateBridge(n, &newNetwork, usedNetworks)
if err != nil {
return nil, err
}
@@ -110,7 +78,7 @@ func (n *cniNetwork) networkCreate(newNetwork types.Network, defaultNet bool) (*
}
for i := range newNetwork.Subnets {
- err := validateSubnet(&newNetwork.Subnets[i], !newNetwork.Internal, usedNetworks)
+ err := internalutil.ValidateSubnet(&newNetwork.Subnets[i], !newNetwork.Internal, usedNetworks)
if err != nil {
return nil, err
}
@@ -223,7 +191,7 @@ func createIPMACVLAN(network *types.Network) error {
return errors.New("internal is not supported with macvlan")
}
if network.NetworkInterface != "" {
- interfaceNames, err := util.GetLiveNetworkNames()
+ interfaceNames, err := internalutil.GetLiveNetworkNames()
if err != nil {
return err
}
@@ -238,107 +206,3 @@ func createIPMACVLAN(network *types.Network) error {
}
return nil
}
-
-func (n *cniNetwork) createBridge(network *types.Network, usedNetworks []*net.IPNet) error {
- if network.NetworkInterface != "" {
- bridges := n.getBridgeInterfaceNames()
- if pkgutil.StringInSlice(network.NetworkInterface, bridges) {
- return errors.Errorf("bridge name %s already in use", network.NetworkInterface)
- }
- if !define.NameRegex.MatchString(network.NetworkInterface) {
- return errors.Wrapf(define.RegexError, "bridge name %s invalid", network.NetworkInterface)
- }
- } else {
- var err error
- network.NetworkInterface, err = n.getFreeDeviceName()
- if err != nil {
- return err
- }
- }
-
- if len(network.Subnets) == 0 {
- freeSubnet, err := n.getFreeIPv4NetworkSubnet(usedNetworks)
- if err != nil {
- return err
- }
- network.Subnets = append(network.Subnets, *freeSubnet)
- }
- // ipv6 enabled means dual stack, check if we already have
- // a ipv4 or ipv6 subnet and add one if not.
- if network.IPv6Enabled {
- ipv4 := false
- ipv6 := false
- for _, subnet := range network.Subnets {
- if util.IsIPv6(subnet.Subnet.IP) {
- ipv6 = true
- }
- if util.IsIPv4(subnet.Subnet.IP) {
- ipv4 = true
- }
- }
- if !ipv4 {
- freeSubnet, err := n.getFreeIPv4NetworkSubnet(usedNetworks)
- if err != nil {
- return err
- }
- network.Subnets = append(network.Subnets, *freeSubnet)
- }
- if !ipv6 {
- freeSubnet, err := n.getFreeIPv6NetworkSubnet(usedNetworks)
- if err != nil {
- return err
- }
- network.Subnets = append(network.Subnets, *freeSubnet)
- }
- }
- network.IPAMOptions["driver"] = types.HostLocalIPAMDriver
- return nil
-}
-
-// validateSubnet will validate a given Subnet. It checks if the
-// given gateway and lease range are part of this subnet. If the
-// gateway is empty and addGateway is true it will get the first
-// available ip in the subnet assigned.
-func validateSubnet(s *types.Subnet, addGateway bool, usedNetworks []*net.IPNet) error {
- if s == nil {
- return errors.New("subnet is nil")
- }
- if s.Subnet.IP == nil {
- return errors.New("subnet ip is nil")
- }
-
- // Reparse to ensure subnet is valid.
- // Do not use types.ParseCIDR() because we want the ip to be
- // the network address and not a random ip in the subnet.
- _, net, err := net.ParseCIDR(s.Subnet.String())
- if err != nil {
- return errors.Wrap(err, "subnet invalid")
- }
-
- // check that the new subnet does not conflict with existing ones
- if util.NetworkIntersectsWithNetworks(net, usedNetworks) {
- return errors.Errorf("subnet %s is already used on the host or by another config", net.String())
- }
-
- s.Subnet = types.IPNet{IPNet: *net}
- if s.Gateway != nil {
- if !s.Subnet.Contains(s.Gateway) {
- return errors.Errorf("gateway %s not in subnet %s", s.Gateway, &s.Subnet)
- }
- } else if addGateway {
- ip, err := util.FirstIPInSubnet(net)
- if err != nil {
- return err
- }
- s.Gateway = ip
- }
- if s.LeaseRange != nil {
- if s.LeaseRange.StartIP != nil && !s.Subnet.Contains(s.LeaseRange.StartIP) {
- return errors.Errorf("lease range start ip %s not in subnet %s", s.LeaseRange.StartIP, &s.Subnet)
- }
- if s.LeaseRange.EndIP != nil && !s.Subnet.Contains(s.LeaseRange.EndIP) {
- return errors.Errorf("lease range end ip %s not in subnet %s", s.LeaseRange.EndIP, &s.Subnet)
- }
- }
- return nil
-}
diff --git a/libpod/network/cni/network.go b/libpod/network/cni/network.go
index a37a84373..3e9cdaa47 100644
--- a/libpod/network/cni/network.go
+++ b/libpod/network/cni/network.go
@@ -6,8 +6,6 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
- "fmt"
- "net"
"os"
"strings"
"time"
@@ -15,8 +13,6 @@ import (
"github.com/containernetworking/cni/libcni"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/network/types"
- "github.com/containers/podman/v3/libpod/network/util"
- pkgutil "github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage/pkg/lockfile"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -242,111 +238,29 @@ func getNetworkIDFromName(name string) string {
return hex.EncodeToString(hash[:])
}
-// getFreeIPv6NetworkSubnet returns a unused ipv4 subnet
-func (n *cniNetwork) getFreeIPv4NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) {
- // the default podman network is 10.88.0.0/16
- // start locking for free /24 networks
- network := &net.IPNet{
- IP: net.IP{10, 89, 0, 0},
- Mask: net.IPMask{255, 255, 255, 0},
- }
-
- // TODO: make sure to not use public subnets
- for {
- if intersectsConfig := util.NetworkIntersectsWithNetworks(network, usedNetworks); !intersectsConfig {
- logrus.Debugf("found free ipv4 network subnet %s", network.String())
- return &types.Subnet{
- Subnet: types.IPNet{IPNet: *network},
- }, nil
- }
- var err error
- network, err = util.NextSubnet(network)
- if err != nil {
- return nil, err
- }
- }
-}
+// Implement the NetUtil interface for easy code sharing with other network interfaces.
-// getFreeIPv6NetworkSubnet returns a unused ipv6 subnet
-func (n *cniNetwork) getFreeIPv6NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) {
- // FIXME: Is 10000 fine as limit? We should prevent an endless loop.
- for i := 0; i < 10000; i++ {
- // RFC4193: Choose the ipv6 subnet random and NOT sequentially.
- network, err := util.GetRandomIPv6Subnet()
- if err != nil {
- return nil, err
- }
- if intersectsConfig := util.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")
-}
-
-// getUsedSubnets returns a list of all used subnets by network
-// configs and interfaces on the host.
-func (n *cniNetwork) getUsedSubnets() ([]*net.IPNet, error) {
- // first, load all used subnets from network configs
- subnets := make([]*net.IPNet, 0, len(n.networks))
+// ForEach call the given function for each network
+func (n *cniNetwork) ForEach(run func(types.Network)) {
for _, val := range n.networks {
- for i := range val.libpodNet.Subnets {
- subnets = append(subnets, &val.libpodNet.Subnets[i].Subnet.IPNet)
- }
+ run(*val.libpodNet)
}
- // second, load networks from the current system
- liveSubnets, err := util.GetLiveNetworkSubnets()
- if err != nil {
- return nil, err
- }
- return append(subnets, liveSubnets...), nil
}
-// getFreeDeviceName returns a free device name which can
-// be used for new configs as name and bridge interface name
-func (n *cniNetwork) getFreeDeviceName() (string, error) {
- bridgeNames := n.getBridgeInterfaceNames()
- netNames := n.getUsedNetworkNames()
- liveInterfaces, err := util.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", cniDeviceName, i)
- if !pkgutil.StringInSlice(deviceName, names) {
- logrus.Debugf("found free device name %s", deviceName)
- return deviceName, nil
- }
- }
- return "", errors.New("could not find free device name, to many iterations")
+// Len return the number of networks
+func (n *cniNetwork) Len() int {
+ return len(n.networks)
}
-// getUsedNetworkNames returns all network names already used
-// by network configs
-func (n *cniNetwork) getUsedNetworkNames() []string {
- names := make([]string, 0, len(n.networks))
- for _, val := range n.networks {
- names = append(names, val.libpodNet.Name)
- }
- return names
+// DefaultInterfaceName return the default cni bridge name, must be suffixed with a number.
+func (n *cniNetwork) DefaultInterfaceName() string {
+ return cniDeviceName
}
-// getUsedNetworkNames returns all bridge device names already used
-// by network configs
-func (n *cniNetwork) getBridgeInterfaceNames() []string {
- names := make([]string, 0, len(n.networks))
- for _, val := range n.networks {
- if val.libpodNet.Driver == types.BridgeNetworkDriver {
- names = append(names, val.libpodNet.NetworkInterface)
- }
+func (n *cniNetwork) Network(nameOrID string) (*types.Network, error) {
+ network, err := n.getNetwork(nameOrID)
+ if err != nil {
+ return nil, err
}
- return names
+ return network.libpodNet, err
}
diff --git a/libpod/network/cni/run.go b/libpod/network/cni/run.go
index 7795dfeeb..667ed3ab1 100644
--- a/libpod/network/cni/run.go
+++ b/libpod/network/cni/run.go
@@ -13,6 +13,7 @@ import (
types040 "github.com/containernetworking/cni/pkg/types/040"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containers/podman/v3/libpod/define"
+ "github.com/containers/podman/v3/libpod/network/internal/util"
"github.com/containers/podman/v3/libpod/network/types"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
@@ -30,24 +31,9 @@ func (n *cniNetwork) Setup(namespacePath string, options types.SetupOptions) (ma
return nil, err
}
- if namespacePath == "" {
- return nil, errors.New("namespacePath is empty")
- }
- if options.ContainerID == "" {
- return nil, errors.New("ContainerID is empty")
- }
- if len(options.Networks) == 0 {
- return nil, errors.New("must specify at least one network")
- }
- for name, netOpts := range options.Networks {
- network := n.networks[name]
- if network == nil {
- return nil, errors.Wrapf(define.ErrNoSuchNetwork, "network %s", name)
- }
- err := validatePerNetworkOpts(network, netOpts)
- if err != nil {
- return nil, err
- }
+ err = util.ValidateSetupOptions(n, namespacePath, options)
+ if err != nil {
+ return nil, err
}
// set the loopback adapter up in the container netns
@@ -172,23 +158,6 @@ func CNIResultToStatus(res cnitypes.Result) (types.StatusBlock, error) {
return result, nil
}
-// validatePerNetworkOpts checks that all given static ips are in a subnet on this network
-func validatePerNetworkOpts(network *network, netOpts types.PerNetworkOptions) error {
- if netOpts.InterfaceName == "" {
- return errors.Errorf("interface name on network %s is empty", network.libpodNet.Name)
- }
-outer:
- for _, ip := range netOpts.StaticIPs {
- for _, s := range network.libpodNet.Subnets {
- if s.Subnet.Contains(ip) {
- continue outer
- }
- }
- return errors.Errorf("requested static ip %s not in any subnet on network %s", ip.String(), network.libpodNet.Name)
- }
- return nil
-}
-
func getRuntimeConfig(netns, conName, conID, networkName string, ports []cniPortMapEntry, opts types.PerNetworkOptions) *libcni.RuntimeConf {
rt := &libcni.RuntimeConf{
ContainerID: conID,
diff --git a/libpod/network/cni/run_test.go b/libpod/network/cni/run_test.go
index 3169cd0eb..6c54f82ef 100644
--- a/libpod/network/cni/run_test.go
+++ b/libpod/network/cni/run_test.go
@@ -1232,7 +1232,7 @@ var _ = Describe("run CNI", func() {
}
_, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
Expect(err).To(HaveOccurred())
- Expect(err.Error()).To(ContainSubstring("network somenet: network not found"))
+ Expect(err.Error()).To(ContainSubstring("unable to find network with name or ID somenet: network not found"))
})
})