spiegel_podman/libpod/network/netavark/config.go
Paul Holzinger eaae294628
netavark network interface
Implement a new network interface for netavark.
For now only bridge networking is supported.
The interface can create/list/inspect/remove networks. For setup and
teardown netavark will be invoked.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2021-11-11 15:54:02 +01:00

210 lines
5.6 KiB
Go

// +build linux
package netavark
import (
"encoding/json"
"net"
"os"
"path/filepath"
"time"
"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/storage/pkg/stringid"
"github.com/pkg/errors"
)
// NetworkCreate will take a partial filled Network and fill the
// missing fields. It creates the Network and returns the full Network.
func (n *netavarkNetwork) NetworkCreate(net types.Network) (types.Network, error) {
n.lock.Lock()
defer n.lock.Unlock()
err := n.loadNetworks()
if err != nil {
return types.Network{}, err
}
network, err := n.networkCreate(net, false)
if err != nil {
return types.Network{}, err
}
// add the new network to the map
n.networks[network.Name] = network
return *network, nil
}
func (n *netavarkNetwork) networkCreate(newNetwork types.Network, defaultNet bool) (*types.Network, error) {
// if no driver is set use the default one
if newNetwork.Driver == "" {
newNetwork.Driver = types.DefaultNetworkDriver
}
if !defaultNet {
// 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")
}
// generate random network ID
var i int
for i = 0; i < 1000; i++ {
id := stringid.GenerateNonCryptoID()
if _, err := n.getNetwork(id); err != nil {
newNetwork.ID = id
break
}
}
if i == 1000 {
return nil, errors.New("failed to create random network ID")
}
}
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.
// The default network should not be validated against used subnets, we have to ensure
// that this network can always be created even when a subnet is already used on the host.
// This could happen if you run a container on this net, then the cni interface will be
// created on the host and "block" this subnet from being used again.
// Therefore the next podman command tries to create the default net again and it would
// fail because it thinks the network is used on the host.
var usedNetworks []*net.IPNet
if !defaultNet {
usedNetworks, err = internalutil.GetUsedSubnets(n)
if err != nil {
return nil, err
}
}
switch newNetwork.Driver {
case types.BridgeNetworkDriver:
err = internalutil.CreateBridge(n, &newNetwork, usedNetworks)
if err != nil {
return nil, err
}
// validate the given options, we do not need them but just check to make sure they are valid
for key, value := range newNetwork.Options {
switch key {
case "mtu":
_, err = internalutil.ParseMTU(value)
if err != nil {
return nil, err
}
case "vlan":
_, err = internalutil.ParseVlan(value)
if err != nil {
return nil, err
}
default:
return nil, errors.Errorf("unsupported network option %s", key)
}
}
default:
return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported driver %s", newNetwork.Driver)
}
err = internalutil.ValidateSubnets(&newNetwork, usedNetworks)
if err != nil {
return nil, err
}
// FIXME: If we have a working solution for internal networks with dns this check should be removed.
if newNetwork.DNSEnabled && newNetwork.Internal {
return nil, errors.New("cannot set internal and dns enabled")
}
newNetwork.Created = time.Now()
if !defaultNet {
confPath := filepath.Join(n.networkConfigDir, newNetwork.Name+".json")
f, err := os.Create(confPath)
if err != nil {
return nil, err
}
enc := json.NewEncoder(f)
enc.SetIndent("", " ")
err = enc.Encode(newNetwork)
if err != nil {
return nil, err
}
}
return &newNetwork, nil
}
// NetworkRemove will remove the Network with the given name or ID.
// It does not ensure that the network is unused.
func (n *netavarkNetwork) NetworkRemove(nameOrID string) error {
n.lock.Lock()
defer n.lock.Unlock()
err := n.loadNetworks()
if err != nil {
return err
}
network, err := n.getNetwork(nameOrID)
if err != nil {
return err
}
// Removing the default network is not allowed.
if network.Name == n.defaultNetwork {
return errors.Errorf("default network %s cannot be removed", n.defaultNetwork)
}
file := filepath.Join(n.networkConfigDir, network.Name+".json")
// make sure to not error for ErrNotExist
if err := os.Remove(file); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
delete(n.networks, network.Name)
return nil
}
// NetworkList will return all known Networks. Optionally you can
// supply a list of filter functions. Only if a network matches all
// functions it is returned.
func (n *netavarkNetwork) NetworkList(filters ...types.FilterFunc) ([]types.Network, error) {
n.lock.Lock()
defer n.lock.Unlock()
err := n.loadNetworks()
if err != nil {
return nil, err
}
networks := make([]types.Network, 0, len(n.networks))
outer:
for _, net := range n.networks {
for _, filter := range filters {
// All filters have to match, if one does not match we can skip to the next network.
if !filter(*net) {
continue outer
}
}
networks = append(networks, *net)
}
return networks, nil
}
// NetworkInspect will return the Network with the given name or ID.
func (n *netavarkNetwork) NetworkInspect(nameOrID string) (types.Network, error) {
n.lock.Lock()
defer n.lock.Unlock()
err := n.loadNetworks()
if err != nil {
return types.Network{}, err
}
network, err := n.getNetwork(nameOrID)
if err != nil {
return types.Network{}, err
}
return *network, nil
}