summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
authorOpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com>2021-09-15 16:11:14 -0400
committerGitHub <noreply@github.com>2021-09-15 16:11:14 -0400
commit5f41ffdd194a828625b3bb6ec55ed87d2830fe58 (patch)
treea625a713dc2205ac80122883f3532fc31bfb8fe2 /libpod
parent505c9718cc717433780be8240250a247253ba34f (diff)
parent5e8309464aea005fbc1604c304a94760aff4ee9a (diff)
downloadpodman-5f41ffdd194a828625b3bb6ec55ed87d2830fe58.tar.gz
podman-5f41ffdd194a828625b3bb6ec55ed87d2830fe58.tar.bz2
podman-5f41ffdd194a828625b3bb6ec55ed87d2830fe58.zip
Merge pull request #11322 from Luap99/network-libpod
Wire network interface into libpod
Diffstat (limited to 'libpod')
-rw-r--r--libpod/boltdb_state.go2
-rw-r--r--libpod/common_test.go4
-rw-r--r--libpod/container.go130
-rw-r--r--libpod/container_config.go4
-rw-r--r--libpod/container_internal.go79
-rw-r--r--libpod/container_internal_linux.go118
-rw-r--r--libpod/info.go4
-rw-r--r--libpod/kube.go4
-rw-r--r--libpod/network/cni/cni_conversion.go3
-rw-r--r--libpod/network/cni/config.go103
-rw-r--r--libpod/network/cni/config_test.go35
-rw-r--r--libpod/network/cni/network.go32
-rw-r--r--libpod/network/cni/run.go7
-rw-r--r--libpod/network/cni/run_test.go4
-rw-r--r--libpod/network/config.go159
-rw-r--r--libpod/network/create.go310
-rw-r--r--libpod/network/create_test.go130
-rw-r--r--libpod/network/devices.go59
-rw-r--r--libpod/network/files.go211
-rw-r--r--libpod/network/ip.go19
-rw-r--r--libpod/network/lock.go35
-rw-r--r--libpod/network/netconflist.go312
-rw-r--r--libpod/network/netconflist_test.go109
-rw-r--r--libpod/network/network.go288
-rw-r--r--libpod/network/network_test.go35
-rw-r--r--libpod/network/subnet.go78
-rw-r--r--libpod/network/subnet_test.go97
-rw-r--r--libpod/network/types/network.go27
-rw-r--r--libpod/network/util/filters.go40
-rw-r--r--libpod/networking_linux.go511
-rw-r--r--libpod/networking_slirp4netns.go17
-rw-r--r--libpod/oci_util.go4
-rw-r--r--libpod/options.go11
-rw-r--r--libpod/runtime.go37
-rw-r--r--libpod/runtime_ctr.go5
-rw-r--r--libpod/util.go6
36 files changed, 608 insertions, 2421 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index 5df3e8961..56b4bafd3 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -1223,7 +1223,7 @@ func (s *BoltState) NetworkConnect(ctr *Container, network string, aliases []str
}
ctrNetworks := ctr.config.Networks
if len(ctrNetworks) == 0 {
- ctrNetworks = []string{ctr.runtime.netPlugin.GetDefaultNetworkName()}
+ ctrNetworks = []string{ctr.runtime.config.Network.DefaultNetwork}
}
// Copy in all the container's CNI networks
for _, net := range ctrNetworks {
diff --git a/libpod/common_test.go b/libpod/common_test.go
index 4c419cfa8..4662a33bd 100644
--- a/libpod/common_test.go
+++ b/libpod/common_test.go
@@ -10,7 +10,7 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/lock"
- "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/containers/podman/v3/libpod/network/types"
"github.com/opencontainers/runtime-tools/generate"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -41,7 +41,7 @@ func getTestContainer(id, name string, manager lock.Manager) (*Container, error)
ContainerNetworkConfig: ContainerNetworkConfig{
DNSServer: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.2.2")},
DNSSearch: []string{"example.com", "example.example.com"},
- PortMappings: []ocicni.PortMapping{
+ PortMappings: []types.OCICNIPortMapping{
{
HostPort: 80,
ContainerPort: 90,
diff --git a/libpod/container.go b/libpod/container.go
index a4bbb5dd0..cf727926c 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -8,14 +8,14 @@ import (
"os"
"time"
- "github.com/containernetworking/cni/pkg/types"
cnitypes "github.com/containernetworking/cni/pkg/types/current"
"github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/manifest"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/lock"
+ "github.com/containers/podman/v3/libpod/network/cni"
+ "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/storage"
- "github.com/cri-o/ocicni/pkg/ocicni"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -114,14 +114,11 @@ type Container struct {
rootlessPortSyncR *os.File
rootlessPortSyncW *os.File
- // A restored container should have the same IP address as before
- // being checkpointed. If requestedIP is set it will be used instead
- // of config.StaticIP.
- requestedIP net.IP
- // A restored container should have the same MAC address as before
- // being checkpointed. If requestedMAC is set it will be used instead
- // of config.StaticMAC.
- requestedMAC net.HardwareAddr
+ // perNetworkOpts should be set when you want to use special network
+ // options when calling network setup/teardown. This should be used for
+ // container restore or network reload for example. Leave this nil if
+ // the settings from the container config should be used.
+ perNetworkOpts map[string]types.PerNetworkOptions
// This is true if a container is restored from a checkpoint.
restoreFromCheckpoint bool
@@ -173,11 +170,20 @@ type ContainerState struct {
// Podman.
// These are DEPRECATED and will be removed in a future release.
LegacyExecSessions map[string]*legacyExecSession `json:"execSessions,omitempty"`
- // NetworkStatus contains the configuration results for all networks
+ // NetworkStatusOld contains the configuration results for all networks
// the pod is attached to. Only populated if we created a network
// namespace for the container, and the network namespace is currently
- // active
- NetworkStatus []*cnitypes.Result `json:"networkResults,omitempty"`
+ // active.
+ // These are DEPRECATED and will be removed in a future release.
+ // This field is only used for backwarts compatibility.
+ NetworkStatusOld []*cnitypes.Result `json:"networkResults,omitempty"`
+ // NetworkStatus contains the network Status for all networks
+ // the container is attached to. Only populated if we created a network
+ // namespace for the container, and the network namespace is currently
+ // active.
+ // To read this field use container.getNetworkStatus() instead, this will
+ // take care of migrating the old DEPRECATED network status to the new format.
+ NetworkStatus map[string]types.StatusBlock `json:"networkStatus,omitempty"`
// BindMounts contains files that will be bind-mounted into the
// container when it is mounted.
// These include /etc/hosts and /etc/resolv.conf
@@ -454,7 +460,7 @@ func (c *Container) NewNetNS() bool {
// PortMappings returns the ports that will be mapped into a container if
// a new network namespace is created
// If NewNetNS() is false, this value is unused
-func (c *Container) PortMappings() ([]ocicni.PortMapping, error) {
+func (c *Container) PortMappings() ([]types.OCICNIPortMapping, error) {
// First check if the container belongs to a network namespace (like a pod)
if len(c.config.NetNsCtr) > 0 {
netNsCtr, err := c.runtime.GetContainer(c.config.NetNsCtr)
@@ -788,66 +794,6 @@ func (c *Container) ExecSession(id string) (*ExecSession, error) {
return returnSession, nil
}
-// IPs retrieves a container's IP address(es)
-// This will only be populated if the container is configured to created a new
-// network namespace, and that namespace is presently active
-func (c *Container) IPs() ([]net.IPNet, error) {
- if !c.batched {
- c.lock.Lock()
- defer c.lock.Unlock()
-
- if err := c.syncContainer(); err != nil {
- return nil, err
- }
- }
-
- if !c.config.CreateNetNS {
- return nil, errors.Wrapf(define.ErrInvalidArg, "container %s network namespace is not managed by libpod", c.ID())
- }
-
- ips := make([]net.IPNet, 0)
-
- for _, r := range c.state.NetworkStatus {
- for _, ip := range r.IPs {
- ips = append(ips, ip.Address)
- }
- }
-
- return ips, nil
-}
-
-// Routes retrieves a container's routes
-// This will only be populated if the container is configured to created a new
-// network namespace, and that namespace is presently active
-func (c *Container) Routes() ([]types.Route, error) {
- if !c.batched {
- c.lock.Lock()
- defer c.lock.Unlock()
-
- if err := c.syncContainer(); err != nil {
- return nil, err
- }
- }
-
- if !c.config.CreateNetNS {
- return nil, errors.Wrapf(define.ErrInvalidArg, "container %s network namespace is not managed by libpod", c.ID())
- }
-
- routes := make([]types.Route, 0)
-
- for _, r := range c.state.NetworkStatus {
- for _, route := range r.Routes {
- newRoute := types.Route{
- Dst: route.Dst,
- GW: route.GW,
- }
- routes = append(routes, newRoute)
- }
- }
-
- return routes, nil
-}
-
// BindMounts retrieves bind mounts that were created by libpod and will be
// added to the container
// All these mounts except /dev/shm are ignored if a mount in the given spec has
@@ -1230,7 +1176,7 @@ func (c *Container) networks() ([]string, bool, error) {
networks, err := c.runtime.state.GetNetworks(c)
if err != nil && errors.Cause(err) == define.ErrNoSuchNetwork {
if len(c.config.Networks) == 0 && c.config.NetMode.IsBridge() {
- return []string{c.runtime.netPlugin.GetDefaultNetworkName()}, true, nil
+ return []string{c.runtime.config.Network.DefaultNetwork}, true, nil
}
return c.config.Networks, false, nil
}
@@ -1267,3 +1213,37 @@ func (d ContainerNetworkDescriptions) getInterfaceByName(networkName string) (st
}
return fmt.Sprintf("eth%d", val), exists
}
+
+// getNetworkStatus get the current network status from the state. If the container
+// still uses the old network status it is converted to the new format. This function
+// should be used instead of reading c.state.NetworkStatus directly.
+func (c *Container) getNetworkStatus() map[string]types.StatusBlock {
+ if c.state.NetworkStatus != nil {
+ return c.state.NetworkStatus
+ }
+ if c.state.NetworkStatusOld != nil {
+ // Note: NetworkStatusOld does not contain the network names so we get them extra
+ // Generally the order should be the same
+ networks, _, err := c.networks()
+ if err != nil {
+ return nil
+ }
+ if len(networks) != len(c.state.NetworkStatusOld) {
+ return nil
+ }
+ result := make(map[string]types.StatusBlock, len(c.state.NetworkStatusOld))
+ for i := range c.state.NetworkStatusOld {
+ status, err := cni.CNIResultToStatus(c.state.NetworkStatusOld[i])
+ if err != nil {
+ return nil
+ }
+ result[networks[i]] = status
+ }
+ c.state.NetworkStatus = result
+ _ = c.save()
+ // TODO remove debug for final version
+ logrus.Debugf("converted old network result to new result %v", result)
+ return result
+ }
+ return nil
+}
diff --git a/libpod/container_config.go b/libpod/container_config.go
index a2c989a1a..0374c25fe 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -6,9 +6,9 @@ import (
"github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/manifest"
+ "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/pkg/namespaces"
"github.com/containers/storage"
- "github.com/cri-o/ocicni/pkg/ocicni"
spec "github.com/opencontainers/runtime-spec/specs-go"
)
@@ -230,7 +230,7 @@ type ContainerNetworkConfig struct {
// PortMappings are the ports forwarded to the container's network
// namespace
// These are not used unless CreateNetNS is true
- PortMappings []ocicni.PortMapping `json:"portMappings,omitempty"`
+ PortMappings []types.OCICNIPortMapping `json:"portMappings,omitempty"`
// ExposedPorts are the ports which are exposed but not forwarded
// into the container.
// The map key is the port and the string slice contains the protocols,
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 1033729ae..9ac2cd5bd 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -644,17 +644,8 @@ func (c *Container) refresh() error {
}
c.lock = lock
- // Try to delete any lingering IP allocations.
- // If this fails, just log and ignore.
- // I'm a little concerned that this is so far down in refresh() and we
- // could fail before getting to it - but the worst that would happen is
- // that Inspect() would return info on IPs we no longer own.
- if len(c.state.NetworkStatus) > 0 {
- if err := c.removeIPv4Allocations(); err != nil {
- logrus.Errorf("Error removing IP allocations for container %s: %v", c.ID(), err)
- }
- }
c.state.NetworkStatus = nil
+ c.state.NetworkStatusOld = nil
if err := c.save(); err != nil {
return errors.Wrapf(err, "error refreshing state for container %s", c.ID())
@@ -668,57 +659,6 @@ func (c *Container) refresh() error {
return nil
}
-// Try and remove IP address allocations. Presently IPv4 only.
-// Should be safe as rootless because NetworkStatus should only be populated if
-// CNI is running.
-func (c *Container) removeIPv4Allocations() error {
- cniNetworksDir, err := getCNINetworksDir()
- if err != nil {
- return err
- }
-
- if len(c.state.NetworkStatus) == 0 {
- return nil
- }
-
- cniDefaultNetwork := ""
- if c.runtime.netPlugin != nil {
- cniDefaultNetwork = c.runtime.netPlugin.GetDefaultNetworkName()
- }
-
- networks, _, err := c.networks()
- if err != nil {
- return err
- }
-
- if len(networks) != len(c.state.NetworkStatus) {
- return errors.Wrapf(define.ErrInternal, "network mismatch: asked to join %d CNI networks but got %d CNI results", len(networks), len(c.state.NetworkStatus))
- }
-
- for index, result := range c.state.NetworkStatus {
- for _, ctrIP := range result.IPs {
- if ctrIP.Version != "4" {
- continue
- }
- candidate := ""
- if len(networks) > 0 {
- // CNI returns networks in order we passed them.
- // So our index into results should be our index
- // into networks.
- candidate = filepath.Join(cniNetworksDir, networks[index], ctrIP.Address.IP.String())
- } else {
- candidate = filepath.Join(cniNetworksDir, cniDefaultNetwork, ctrIP.Address.IP.String())
- }
- logrus.Debugf("Going to try removing IP address reservation file %q for container %s", candidate, c.ID())
- if err := os.Remove(candidate); err != nil && !os.IsNotExist(err) {
- return errors.Wrapf(err, "error removing CNI IP reservation file %q for container %s", candidate, c.ID())
- }
- }
- }
-
- return nil
-}
-
// Remove conmon attach socket and terminal resize FIFO
// This is necessary for restarting containers
func (c *Container) removeConmonFiles() error {
@@ -1017,11 +957,9 @@ func (c *Container) completeNetworkSetup() error {
}
state := c.state
// collect any dns servers that cni tells us to use (dnsname)
- for _, cni := range state.NetworkStatus {
- if cni.DNS.Nameservers != nil {
- for _, server := range cni.DNS.Nameservers {
- outResolvConf = append(outResolvConf, fmt.Sprintf("nameserver %s", server))
- }
+ for _, status := range c.getNetworkStatus() {
+ for _, server := range status.DNSServerIPs {
+ outResolvConf = append(outResolvConf, fmt.Sprintf("nameserver %s", server))
}
}
// check if we have a bindmount for /etc/hosts
@@ -1062,9 +1000,12 @@ func (c *Container) completeNetworkSetup() error {
func (c *Container) cniHosts() string {
var hosts string
- if len(c.state.NetworkStatus) > 0 && len(c.state.NetworkStatus[0].IPs) > 0 {
- ipAddress := strings.Split(c.state.NetworkStatus[0].IPs[0].Address.String(), "/")[0]
- hosts += fmt.Sprintf("%s\t%s %s\n", ipAddress, c.Hostname(), c.Config().Name)
+ for _, status := range c.getNetworkStatus() {
+ for _, netInt := range status.Interfaces {
+ for _, netAddress := range netInt.Networks {
+ hosts += fmt.Sprintf("%s\t%s %s\n", netAddress.Subnet.IP.String(), c.Hostname(), c.Config().Name)
+ }
+ }
}
return hosts
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 4194a0d93..0557b30d0 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -22,7 +22,6 @@ import (
metadata "github.com/checkpoint-restore/checkpointctl/lib"
cdi "github.com/container-orchestrated-devices/container-device-interface/pkg"
- cnitypes "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containers/buildah/pkg/chrootuser"
"github.com/containers/buildah/pkg/overlay"
@@ -34,6 +33,7 @@ import (
"github.com/containers/common/pkg/umask"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/events"
+ "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/pkg/annotations"
"github.com/containers/podman/v3/pkg/cgroups"
"github.com/containers/podman/v3/pkg/checkpoint/crutils"
@@ -81,7 +81,7 @@ func (c *Container) prepare() error {
var (
wg sync.WaitGroup
netNS ns.NetNS
- networkStatus []*cnitypes.Result
+ networkStatus map[string]types.StatusBlock
createNetNSErr, mountStorageErr error
mountPoint string
tmpStateLock sync.Mutex
@@ -263,6 +263,7 @@ func (c *Container) cleanupNetwork() error {
c.state.NetNS = nil
c.state.NetworkStatus = nil
+ c.state.NetworkStatusOld = nil
if c.valid {
return c.save()
@@ -1121,7 +1122,8 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
// Save network.status. This is needed to restore the container with
// the same IP. Currently limited to one IP address in a container
// with one interface.
- if _, err := metadata.WriteJSONFile(c.state.NetworkStatus, c.bundlePath(), metadata.NetworkStatusFile); err != nil {
+ // FIXME: will this break something?
+ if _, err := metadata.WriteJSONFile(c.getNetworkStatus(), c.bundlePath(), metadata.NetworkStatusFile); err != nil {
return err
}
@@ -1261,8 +1263,11 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
}
// Read network configuration from checkpoint
- // Currently only one interface with one IP is supported.
- networkStatus, _, err := metadata.ReadContainerCheckpointNetworkStatus(c.bundlePath())
+ var netStatus map[string]types.StatusBlock
+ _, err := metadata.ReadJSONFile(&netStatus, c.bundlePath(), metadata.NetworkStatusFile)
+ if err != nil {
+ logrus.Infof("failed to unmarshal network status, cannot restore the same ip/mac: %v", err)
+ }
// If the restored container should get a new name, the IP address of
// the container will not be restored. This assumes that if a new name is
// specified, the container is restored multiple times.
@@ -1271,19 +1276,41 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
// best solution.
if err == nil && options.Name == "" && (!options.IgnoreStaticIP || !options.IgnoreStaticMAC) {
// The file with the network.status does exist. Let's restore the
- // container with the same IP address / MAC address as during checkpointing.
- if !options.IgnoreStaticIP {
- if IP := metadata.GetIPFromNetworkStatus(networkStatus); IP != nil {
- // Tell CNI which IP address we want.
- c.requestedIP = IP
- }
+ // container with the same networks settings as during checkpointing.
+ aliases, err := c.runtime.state.GetAllNetworkAliases(c)
+ if err != nil {
+ return err
}
- if !options.IgnoreStaticMAC {
- if MAC := metadata.GetMACFromNetworkStatus(networkStatus); MAC != nil {
- // Tell CNI which MAC address we want.
- c.requestedMAC = MAC
+ netOpts := make(map[string]types.PerNetworkOptions, len(netStatus))
+ for network, status := range netStatus {
+ perNetOpts := types.PerNetworkOptions{}
+ for name, netInt := range status.Interfaces {
+ perNetOpts = types.PerNetworkOptions{
+ InterfaceName: name,
+ Aliases: aliases[network],
+ }
+ if !options.IgnoreStaticMAC {
+ perNetOpts.StaticMAC = netInt.MacAddress
+ }
+ if !options.IgnoreStaticIP {
+ for _, netAddress := range netInt.Networks {
+ perNetOpts.StaticIPs = append(perNetOpts.StaticIPs, netAddress.Subnet.IP)
+ }
+ }
+ // Normally interfaces have a length of 1, only for some special cni configs we could get more.
+ // For now just use the first interface to get the ips this should be good enough for most cases.
+ break
+ }
+ if perNetOpts.InterfaceName == "" {
+ eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(network)
+ if !exists {
+ return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, network)
+ }
+ perNetOpts.InterfaceName = eth
}
+ netOpts[network] = perNetOpts
}
+ c.perNetworkOpts = netOpts
}
defer func() {
@@ -1785,9 +1812,9 @@ rootless=%d
// generateResolvConf generates a containers resolv.conf
func (c *Container) generateResolvConf() (string, error) {
var (
- nameservers []string
- cniNameServers []string
- cniSearchDomains []string
+ nameservers []string
+ networkNameServers []string
+ networkSearchDomains []string
)
resolvConf := "/etc/resolv.conf"
@@ -1827,22 +1854,27 @@ func (c *Container) generateResolvConf() (string, error) {
}
ipv6 := false
- // Check if CNI gave back and DNS servers for us to add in
- cniResponse := c.state.NetworkStatus
- for _, i := range cniResponse {
- for _, ip := range i.IPs {
- // Note: only using To16() does not work since it also returns a valid ip for ipv4
- if ip.Address.IP.To4() == nil && ip.Address.IP.To16() != nil {
- ipv6 = true
+ // If network status is set check for ipv6 and dns namesevers
+ netStatus := c.getNetworkStatus()
+ for _, status := range netStatus {
+ for _, netInt := range status.Interfaces {
+ for _, netAddress := range netInt.Networks {
+ // Note: only using To16() does not work since it also returns a valid ip for ipv4
+ if netAddress.Subnet.IP.To4() == nil && netAddress.Subnet.IP.To16() != nil {
+ ipv6 = true
+ }
}
}
- if i.DNS.Nameservers != nil {
- cniNameServers = append(cniNameServers, i.DNS.Nameservers...)
- logrus.Debugf("adding nameserver(s) from cni response of '%q'", i.DNS.Nameservers)
+
+ if status.DNSServerIPs != nil {
+ for _, nsIP := range status.DNSServerIPs {
+ networkNameServers = append(networkNameServers, nsIP.String())
+ }
+ logrus.Debugf("adding nameserver(s) from network status of '%q'", status.DNSServerIPs)
}
- if i.DNS.Search != nil {
- cniSearchDomains = append(cniSearchDomains, i.DNS.Search...)
- logrus.Debugf("adding search domain(s) from cni response of '%q'", i.DNS.Search)
+ if status.DNSSearchDomains != nil {
+ networkSearchDomains = append(networkSearchDomains, status.DNSSearchDomains...)
+ logrus.Debugf("adding search domain(s) from network status of '%q'", status.DNSSearchDomains)
}
}
@@ -1882,8 +1914,8 @@ func (c *Container) generateResolvConf() (string, error) {
for _, server := range dnsServers {
nameservers = append(nameservers, server.String())
}
- case len(cniNameServers) > 0:
- nameservers = append(nameservers, cniNameServers...)
+ case len(networkNameServers) > 0:
+ nameservers = append(nameservers, networkNameServers...)
default:
// Make a new resolv.conf
nameservers = resolvconf.GetNameservers(resolv.Content)
@@ -1899,11 +1931,11 @@ func (c *Container) generateResolvConf() (string, error) {
}
var search []string
- if len(c.config.DNSSearch) > 0 || len(c.runtime.config.Containers.DNSSearches) > 0 || len(cniSearchDomains) > 0 {
+ if len(c.config.DNSSearch) > 0 || len(c.runtime.config.Containers.DNSSearches) > 0 || len(networkSearchDomains) > 0 {
if !util.StringInSlice(".", c.config.DNSSearch) {
search = c.runtime.config.Containers.DNSSearches
search = append(search, c.config.DNSSearch...)
- search = append(search, cniSearchDomains...)
+ search = append(search, networkSearchDomains...)
}
} else {
search = resolvconf.GetSearchDomains(resolv.Content)
@@ -2019,20 +2051,22 @@ func (c *Container) getHosts() string {
// Add gateway entry
var depCtr *Container
+ netStatus := c.getNetworkStatus()
if c.config.NetNsCtr != "" {
// ignoring the error because there isn't anything to do
depCtr, _ = c.getRootNetNsDepCtr()
- } else if len(c.state.NetworkStatus) != 0 {
+ } else if len(netStatus) != 0 {
depCtr = c
- } else {
- depCtr = nil
}
if depCtr != nil {
- for _, pluginResultsRaw := range depCtr.state.NetworkStatus {
- pluginResult, _ := cnitypes.GetResult(pluginResultsRaw)
- for _, ip := range pluginResult.IPs {
- hosts += fmt.Sprintf("%s host.containers.internal\n", ip.Gateway)
+ for _, status := range depCtr.getNetworkStatus() {
+ for _, netInt := range status.Interfaces {
+ for _, netAddress := range netInt.Networks {
+ if netAddress.Gateway != nil {
+ hosts += fmt.Sprintf("%s host.containers.internal\n", netAddress.Gateway.String())
+ }
+ }
}
}
} else if c.config.NetMode.IsSlirp4netns() {
diff --git a/libpod/info.go b/libpod/info.go
index 7b60ee46f..36dc8bc2a 100644
--- a/libpod/info.go
+++ b/libpod/info.go
@@ -18,7 +18,6 @@ import (
"github.com/containers/image/v5/pkg/sysregistriesv2"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/linkmode"
- "github.com/containers/podman/v3/libpod/network"
"github.com/containers/podman/v3/pkg/cgroups"
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/storage"
@@ -73,8 +72,7 @@ func (r *Runtime) info() (*define.Info, error) {
volumePlugins = append(volumePlugins, plugin)
}
info.Plugins.Volume = volumePlugins
- // TODO move this into the new network interface
- info.Plugins.Network = []string{network.BridgeNetworkDriver, network.MacVLANNetworkDriver}
+ info.Plugins.Network = r.network.Drivers()
info.Plugins.Log = logDrivers
info.Registries = registries
diff --git a/libpod/kube.go b/libpod/kube.go
index 812bb101b..54e8a7c50 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -10,11 +10,11 @@ import (
"time"
"github.com/containers/podman/v3/libpod/define"
+ "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/pkg/lookup"
"github.com/containers/podman/v3/pkg/namespaces"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/util"
- "github.com/cri-o/ocicni/pkg/ocicni"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
@@ -544,7 +544,7 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, *v1.PodDNS
// ocicniPortMappingToContainerPort takes an ocicni portmapping and converts
// it to a v1.ContainerPort format for kube output
-func ocicniPortMappingToContainerPort(portMappings []ocicni.PortMapping) ([]v1.ContainerPort, error) {
+func ocicniPortMappingToContainerPort(portMappings []types.OCICNIPortMapping) ([]v1.ContainerPort, error) {
containerPorts := make([]v1.ContainerPort, 0, len(portMappings))
for _, p := range portMappings {
var protocol v1.Protocol
diff --git a/libpod/network/cni/cni_conversion.go b/libpod/network/cni/cni_conversion.go
index 09943948b..060794ebe 100644
--- a/libpod/network/cni/cni_conversion.go
+++ b/libpod/network/cni/cni_conversion.go
@@ -185,6 +185,9 @@ func convertIPAMConfToNetwork(network *types.Network, ipam ipamConfig, confPath
s.LeaseRange.StartIP = rangeStart
s.LeaseRange.EndIP = rangeEnd
}
+ if util.IsIPv6(s.Subnet.IP) {
+ network.IPv6Enabled = true
+ }
network.Subnets = append(network.Subnets, s)
}
}
diff --git a/libpod/network/cni/config.go b/libpod/network/cni/config.go
index ee203f80d..d31cd3002 100644
--- a/libpod/network/cni/config.go
+++ b/libpod/network/cni/config.go
@@ -24,7 +24,7 @@ func (n *cniNetwork) NetworkCreate(net types.Network) (types.Network, error) {
if err != nil {
return types.Network{}, err
}
- network, err := n.networkCreate(net, true)
+ network, err := n.networkCreate(net, false)
if err != nil {
return types.Network{}, err
}
@@ -33,89 +33,106 @@ func (n *cniNetwork) NetworkCreate(net types.Network) (types.Network, error) {
return *network.libpodNet, nil
}
-func (n *cniNetwork) networkCreate(net types.Network, writeToDisk bool) (*network, error) {
+// networkCreate will fill out the given network struct and return the new network entry.
+// If defaultNet is true it will not validate against used subnets and it will not write the cni config to disk.
+func (n *cniNetwork) networkCreate(newNetwork types.Network, defaultNet bool) (*network, error) {
// if no driver is set use the default one
- if net.Driver == "" {
- net.Driver = types.DefaultNetworkDriver
+ if newNetwork.Driver == "" {
+ 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 net.ID != "" {
+ if newNetwork.ID != "" {
return nil, errors.Wrap(define.ErrInvalidArg, "ID can not be set for network create")
}
- if net.Labels == nil {
- net.Labels = map[string]string{}
+ if newNetwork.Labels == nil {
+ newNetwork.Labels = map[string]string{}
}
- if net.Options == nil {
- net.Options = map[string]string{}
+ if newNetwork.Options == nil {
+ newNetwork.Options = map[string]string{}
}
- if net.IPAMOptions == nil {
- net.IPAMOptions = map[string]string{}
+ if newNetwork.IPAMOptions == nil {
+ newNetwork.IPAMOptions = map[string]string{}
}
var name string
var err error
// validate the name when given
- if net.Name != "" {
- if !define.NameRegex.MatchString(net.Name) {
- return nil, errors.Wrapf(define.RegexError, "network name %s invalid", net.Name)
+ 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[net.Name]; ok {
- return nil, errors.Wrapf(define.ErrNetworkExists, "network name %s already used", net.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
}
- net.Name = name
+ newNetwork.Name = name
}
- switch net.Driver {
+ // 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 = n.getUsedSubnets()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ switch newNetwork.Driver {
case types.BridgeNetworkDriver:
// if the name was created with getFreeDeviceName set the interface to it as well
- if name != "" && net.NetworkInterface == "" {
- net.NetworkInterface = name
+ if name != "" && newNetwork.NetworkInterface == "" {
+ newNetwork.NetworkInterface = name
}
- err = n.createBridge(&net)
+ err = n.createBridge(&newNetwork, usedNetworks)
if err != nil {
return nil, err
}
case types.MacVLANNetworkDriver:
- err = createMacVLAN(&net)
+ err = createMacVLAN(&newNetwork)
if err != nil {
return nil, err
}
default:
- return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported driver %s", net.Driver)
+ return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported driver %s", newNetwork.Driver)
}
- for i := range net.Subnets {
- err := validateSubnet(&net.Subnets[i], !net.Internal)
+ for i := range newNetwork.Subnets {
+ err := validateSubnet(&newNetwork.Subnets[i], !newNetwork.Internal, usedNetworks)
if err != nil {
return nil, err
}
- if util.IsIPv6(net.Subnets[i].Subnet.IP) {
- net.IPv6Enabled = true
+ if util.IsIPv6(newNetwork.Subnets[i].Subnet.IP) {
+ newNetwork.IPv6Enabled = true
}
}
// generate the network ID
- net.ID = getNetworkIDFromName(net.Name)
+ newNetwork.ID = getNetworkIDFromName(newNetwork.Name)
// FIXME: Should this be a hard error?
- if net.DNSEnabled && net.Internal && hasDNSNamePlugin(n.cniPluginDirs) {
- logrus.Warnf("dnsname and internal networks are incompatible. dnsname plugin not configured for network %s", net.Name)
- net.DNSEnabled = false
+ if newNetwork.DNSEnabled && newNetwork.Internal && hasDNSNamePlugin(n.cniPluginDirs) {
+ logrus.Warnf("dnsname and internal networks are incompatible. dnsname plugin not configured for network %s", newNetwork.Name)
+ newNetwork.DNSEnabled = false
}
- cniConf, path, err := n.createCNIConfigListFromNetwork(&net, writeToDisk)
+ cniConf, path, err := n.createCNIConfigListFromNetwork(&newNetwork, !defaultNet)
if err != nil {
return nil, err
}
- return &network{cniNet: cniConf, libpodNet: &net, filename: path}, nil
+ return &network{cniNet: cniConf, libpodNet: &newNetwork, filename: path}, nil
}
// NetworkRemove will remove the Network with the given name or ID.
@@ -218,7 +235,7 @@ func createMacVLAN(network *types.Network) error {
return nil
}
-func (n *cniNetwork) createBridge(network *types.Network) error {
+func (n *cniNetwork) createBridge(network *types.Network, usedNetworks []*net.IPNet) error {
if network.NetworkInterface != "" {
bridges := n.getBridgeInterfaceNames()
if pkgutil.StringInSlice(network.NetworkInterface, bridges) {
@@ -236,7 +253,7 @@ func (n *cniNetwork) createBridge(network *types.Network) error {
}
if len(network.Subnets) == 0 {
- freeSubnet, err := n.getFreeIPv4NetworkSubnet()
+ freeSubnet, err := n.getFreeIPv4NetworkSubnet(usedNetworks)
if err != nil {
return err
}
@@ -256,14 +273,14 @@ func (n *cniNetwork) createBridge(network *types.Network) error {
}
}
if !ipv4 {
- freeSubnet, err := n.getFreeIPv4NetworkSubnet()
+ freeSubnet, err := n.getFreeIPv4NetworkSubnet(usedNetworks)
if err != nil {
return err
}
network.Subnets = append(network.Subnets, *freeSubnet)
}
if !ipv6 {
- freeSubnet, err := n.getFreeIPv6NetworkSubnet()
+ freeSubnet, err := n.getFreeIPv6NetworkSubnet(usedNetworks)
if err != nil {
return err
}
@@ -278,10 +295,14 @@ func (n *cniNetwork) createBridge(network *types.Network) error {
// 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) error {
+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.
@@ -289,6 +310,12 @@ func validateSubnet(s *types.Subnet, addGateway bool) error {
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) {
diff --git a/libpod/network/cni/config_test.go b/libpod/network/cni/config_test.go
index f67402657..11ad71870 100644
--- a/libpod/network/cni/config_test.go
+++ b/libpod/network/cni/config_test.go
@@ -313,6 +313,14 @@ var _ = Describe("Config", func() {
Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet))
Expect(network1.Subnets[0].Gateway.String()).To(Equal("fdcc::1"))
Expect(network1.Subnets[0].LeaseRange).To(BeNil())
+
+ // reload configs from disk
+ libpodNet, err = getNetworkInterface(cniConfDir, false)
+ Expect(err).To(BeNil())
+ // check the the networks are identical
+ network2, err := libpodNet.NetworkInspect(network1.Name)
+ Expect(err).To(BeNil())
+ Expect(network1).To(Equal(network2))
})
It("create bridge with ipv6 enabled", func() {
@@ -508,6 +516,9 @@ var _ = Describe("Config", func() {
Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1"))
Expect(network1.Subnets[0].LeaseRange.StartIP.String()).To(Equal(startIP))
+ err = libpodNet.NetworkRemove(network1.Name)
+ Expect(err).To(BeNil())
+
endIP := "10.0.0.10"
network = types.Network{
Driver: "bridge",
@@ -529,6 +540,9 @@ var _ = Describe("Config", func() {
Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1"))
Expect(network1.Subnets[0].LeaseRange.EndIP.String()).To(Equal(endIP))
+ err = libpodNet.NetworkRemove(network1.Name)
+ Expect(err).To(BeNil())
+
network = types.Network{
Driver: "bridge",
Subnets: []types.Subnet{
@@ -590,7 +604,7 @@ var _ = Describe("Config", func() {
}
_, err := libpodNet.NetworkCreate(network)
Expect(err).To(HaveOccurred())
- Expect(err.Error()).To(ContainSubstring("subnet invalid"))
+ Expect(err.Error()).To(ContainSubstring("subnet ip is nil"))
})
It("create network with name", func() {
@@ -886,6 +900,25 @@ var _ = Describe("Config", func() {
Expect(err.Error()).To(Equal("default network podman cannot be removed"))
})
+ It("network create with same subnet", func() {
+ subnet := "10.0.0.0/24"
+ n, _ := types.ParseCIDR(subnet)
+ subnet2 := "10.10.0.0/24"
+ n2, _ := types.ParseCIDR(subnet2)
+ network := types.Network{Subnets: []types.Subnet{{Subnet: n}, {Subnet: n2}}}
+ network1, err := libpodNet.NetworkCreate(network)
+ Expect(err).To(BeNil())
+ Expect(network1.Subnets).To(HaveLen(2))
+ network = types.Network{Subnets: []types.Subnet{{Subnet: n}}}
+ _, err = libpodNet.NetworkCreate(network)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("subnet 10.0.0.0/24 is already used on the host or by another config"))
+ network = types.Network{Subnets: []types.Subnet{{Subnet: n2}}}
+ _, err = libpodNet.NetworkCreate(network)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("subnet 10.10.0.0/24 is already used on the host or by another config"))
+ })
+
})
Context("network load valid existing ones", func() {
diff --git a/libpod/network/cni/network.go b/libpod/network/cni/network.go
index fde08a0c6..46e07f780 100644
--- a/libpod/network/cni/network.go
+++ b/libpod/network/cni/network.go
@@ -106,6 +106,12 @@ func NewCNINetworkInterface(conf InitConfig) (types.ContainerNetwork, error) {
return n, nil
}
+// Drivers will return the list of supported network drivers
+// for this interface.
+func (n *cniNetwork) Drivers() []string {
+ return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver}
+}
+
func (n *cniNetwork) loadNetworks() error {
// skip loading networks if they are already loaded
if n.networks != nil {
@@ -179,7 +185,7 @@ func (n *cniNetwork) createDefaultNetwork() (*network, error) {
{Subnet: n.defaultSubnet},
},
}
- return n.networkCreate(net, false)
+ return n.networkCreate(net, true)
}
// getNetwork will lookup a network by name or ID. It returns an
@@ -221,12 +227,7 @@ func getNetworkIDFromName(name string) string {
}
// getFreeIPv6NetworkSubnet returns a unused ipv4 subnet
-func (n *cniNetwork) getFreeIPv4NetworkSubnet() (*types.Subnet, error) {
- networks, err := n.getUsedSubnets()
- if err != nil {
- return nil, err
- }
-
+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{
@@ -236,12 +237,13 @@ func (n *cniNetwork) getFreeIPv4NetworkSubnet() (*types.Subnet, error) {
// TODO: make sure to not use public subnets
for {
- if intersectsConfig := util.NetworkIntersectsWithNetworks(network, networks); !intersectsConfig {
+ 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
@@ -250,12 +252,7 @@ func (n *cniNetwork) getFreeIPv4NetworkSubnet() (*types.Subnet, error) {
}
// getFreeIPv6NetworkSubnet returns a unused ipv6 subnet
-func (n *cniNetwork) getFreeIPv6NetworkSubnet() (*types.Subnet, error) {
- networks, err := n.getUsedSubnets()
- if err != nil {
- return nil, err
- }
-
+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.
@@ -263,7 +260,7 @@ func (n *cniNetwork) getFreeIPv6NetworkSubnet() (*types.Subnet, error) {
if err != nil {
return nil, err
}
- if intersectsConfig := util.NetworkIntersectsWithNetworks(&network, networks); !intersectsConfig {
+ 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},
@@ -279,9 +276,8 @@ func (n *cniNetwork) getUsedSubnets() ([]*net.IPNet, error) {
// first, load all used subnets from network configs
subnets := make([]*net.IPNet, 0, len(n.networks))
for _, val := range n.networks {
- for _, subnet := range val.libpodNet.Subnets {
- // nolint:exportloopref
- subnets = append(subnets, &subnet.Subnet.IPNet)
+ for i := range val.libpodNet.Subnets {
+ subnets = append(subnets, &val.libpodNet.Subnets[i].Subnet.IPNet)
}
}
// second, load networks from the current system
diff --git a/libpod/network/cni/run.go b/libpod/network/cni/run.go
index 14634262c..b69953c4b 100644
--- a/libpod/network/cni/run.go
+++ b/libpod/network/cni/run.go
@@ -114,7 +114,7 @@ func (n *cniNetwork) Setup(namespacePath string, options types.SetupOptions) (ma
}
logrus.Debugf("cni result for container %s network %s: %v", options.ContainerID, name, cnires)
var status types.StatusBlock
- status, retErr = cniResultToStatus(cnires)
+ status, retErr = CNIResultToStatus(cnires)
if retErr != nil {
return nil, retErr
}
@@ -123,8 +123,9 @@ func (n *cniNetwork) Setup(namespacePath string, options types.SetupOptions) (ma
return results, nil
}
-// cniResultToStatus convert the cni result to status block
-func cniResultToStatus(cniResult *current.Result) (types.StatusBlock, error) {
+// CNIResultToStatus convert the cni result to status block
+// nolint:golint
+func CNIResultToStatus(cniResult *current.Result) (types.StatusBlock, error) {
result := types.StatusBlock{}
nameservers := make([]net.IP, 0, len(cniResult.DNS.Nameservers))
for _, nameserver := range cniResult.DNS.Nameservers {
diff --git a/libpod/network/cni/run_test.go b/libpod/network/cni/run_test.go
index 32e88ca61..f6da22a76 100644
--- a/libpod/network/cni/run_test.go
+++ b/libpod/network/cni/run_test.go
@@ -140,6 +140,10 @@ var _ = Describe("run CNI", func() {
Expect(res[defNet].DNSServerIPs).To(BeEmpty())
Expect(res[defNet].DNSSearchDomains).To(BeEmpty())
+ // reload the interface so the networks are reload from disk
+ libpodNet, err := getNetworkInterface(cniConfDir, false)
+ Expect(err).To(BeNil())
+
err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts))
Expect(err).To(BeNil())
})
diff --git a/libpod/network/config.go b/libpod/network/config.go
deleted file mode 100644
index 9a3bc4763..000000000
--- a/libpod/network/config.go
+++ /dev/null
@@ -1,159 +0,0 @@
-package network
-
-import (
- "encoding/json"
- "net"
-
- "github.com/containers/storage/pkg/lockfile"
-)
-
-// TODO once the containers.conf file stuff is worked out, this should be modified
-// to honor defines in the containers.conf as well as overrides?
-
-const (
- // CNIConfigDir is the path where CNI config files exist
- CNIConfigDir = "/etc/cni/net.d"
- // CNIDeviceName is the default network device name and in
- // reality should have an int appended to it (cni-podman4)
- CNIDeviceName = "cni-podman"
- // DefaultPodmanDomainName is used for the dnsname plugin to define
- // a localized domain name for a created network
- DefaultPodmanDomainName = "dns.podman"
- // LockFileName is used for obtaining a lock and is appended
- // to libpod's tmpdir in practice
- LockFileName = "cni.lock"
-)
-
-// CNILock is for preventing name collision and
-// unpredictable results when doing some CNI operations.
-type CNILock struct {
- lockfile.Locker
-}
-
-// GetDefaultPodmanNetwork outputs the default network for podman
-func GetDefaultPodmanNetwork() (*net.IPNet, error) {
- _, n, err := net.ParseCIDR("10.88.1.0/24")
- return n, err
-}
-
-// CNIPlugins is a way of marshalling a CNI network configuration to disk
-type CNIPlugins interface {
- Bytes() ([]byte, error)
-}
-
-// HostLocalBridge describes a configuration for a bridge plugin
-// https://github.com/containernetworking/plugins/tree/master/plugins/main/bridge#network-configuration-reference
-type HostLocalBridge struct {
- PluginType string `json:"type"`
- BrName string `json:"bridge,omitempty"`
- IsGW bool `json:"isGateway"`
- IsDefaultGW bool `json:"isDefaultGateway,omitempty"`
- ForceAddress bool `json:"forceAddress,omitempty"`
- IPMasq bool `json:"ipMasq,omitempty"`
- MTU int `json:"mtu,omitempty"`
- HairpinMode bool `json:"hairpinMode,omitempty"`
- PromiscMode bool `json:"promiscMode,omitempty"`
- Vlan int `json:"vlan,omitempty"`
- IPAM IPAMConfig `json:"ipam"`
-}
-
-// Bytes outputs []byte
-func (h *HostLocalBridge) Bytes() ([]byte, error) {
- return json.MarshalIndent(h, "", "\t")
-}
-
-// IPAMConfig describes an IPAM configuration
-// https://github.com/containernetworking/plugins/tree/master/plugins/ipam/host-local#network-configuration-reference
-type IPAMConfig struct {
- PluginType string `json:"type"`
- Routes []IPAMRoute `json:"routes,omitempty"`
- ResolveConf string `json:"resolveConf,omitempty"`
- DataDir string `json:"dataDir,omitempty"`
- Ranges [][]IPAMLocalHostRangeConf `json:"ranges,omitempty"`
-}
-
-// IPAMLocalHostRangeConf describes the new style IPAM ranges
-type IPAMLocalHostRangeConf struct {
- Subnet string `json:"subnet"`
- RangeStart string `json:"rangeStart,omitempty"`
- RangeEnd string `json:"rangeEnd,omitempty"`
- Gateway string `json:"gateway,omitempty"`
-}
-
-// Bytes outputs the configuration as []byte
-func (i IPAMConfig) Bytes() ([]byte, error) {
- return json.MarshalIndent(i, "", "\t")
-}
-
-// IPAMRoute describes a route in an ipam config
-type IPAMRoute struct {
- Dest string `json:"dst"`
-}
-
-// PortMapConfig describes the default portmapping config
-type PortMapConfig struct {
- PluginType string `json:"type"`
- Capabilities map[string]bool `json:"capabilities"`
-}
-
-// Bytes outputs the configuration as []byte
-func (p PortMapConfig) Bytes() ([]byte, error) {
- return json.MarshalIndent(p, "", "\t")
-}
-
-// MacVLANConfig describes the macvlan config
-type MacVLANConfig struct {
- PluginType string `json:"type"`
- Master string `json:"master"`
- IPAM IPAMConfig `json:"ipam"`
- MTU int `json:"mtu,omitempty"`
-}
-
-// Bytes outputs the configuration as []byte
-func (p MacVLANConfig) Bytes() ([]byte, error) {
- return json.MarshalIndent(p, "", "\t")
-}
-
-// FirewallConfig describes the firewall plugin
-type FirewallConfig struct {
- PluginType string `json:"type"`
- Backend string `json:"backend"`
-}
-
-// Bytes outputs the configuration as []byte
-func (f FirewallConfig) Bytes() ([]byte, error) {
- return json.MarshalIndent(f, "", "\t")
-}
-
-// TuningConfig describes the tuning plugin
-type TuningConfig struct {
- PluginType string `json:"type"`
-}
-
-// Bytes outputs the configuration as []byte
-func (f TuningConfig) Bytes() ([]byte, error) {
- return json.MarshalIndent(f, "", "\t")
-}
-
-// DNSNameConfig describes the dns container name resolution plugin config
-type DNSNameConfig struct {
- PluginType string `json:"type"`
- DomainName string `json:"domainName"`
- Capabilities map[string]bool `json:"capabilities"`
-}
-
-// PodmanMachineConfig enables port handling on the host OS
-type PodmanMachineConfig struct {
- PluginType string `json:"type"`
- Capabilities map[string]bool `json:"capabilities"`
-}
-
-// Bytes outputs the configuration as []byte
-func (d DNSNameConfig) Bytes() ([]byte, error) {
- return json.MarshalIndent(d, "", "\t")
-}
-
-// Bytes outputs the configuration as []byte
-func (p PodmanMachineConfig) Bytes() ([]byte, error) {
- return json.MarshalIndent(p, "", "\t")
-}
diff --git a/libpod/network/create.go b/libpod/network/create.go
deleted file mode 100644
index aca8150b5..000000000
--- a/libpod/network/create.go
+++ /dev/null
@@ -1,310 +0,0 @@
-package network
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "strconv"
-
- "github.com/containernetworking/cni/pkg/version"
- "github.com/containers/common/pkg/config"
- "github.com/containers/podman/v3/pkg/domain/entities"
- "github.com/containers/podman/v3/pkg/util"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-// Create the CNI network
-func Create(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (*entities.NetworkCreateReport, error) {
- var fileName string
- if err := isSupportedDriver(options.Driver); err != nil {
- return nil, err
- }
- // Acquire a lock for CNI
- l, err := acquireCNILock(runtimeConfig)
- if err != nil {
- return nil, err
- }
- defer l.releaseCNILock()
- if len(options.MacVLAN) > 0 || options.Driver == MacVLANNetworkDriver {
- fileName, err = createMacVLAN(name, options, runtimeConfig)
- } else {
- fileName, err = createBridge(name, options, runtimeConfig)
- }
- if err != nil {
- return nil, err
- }
- return &entities.NetworkCreateReport{Filename: fileName}, nil
-}
-
-// validateBridgeOptions validate the bridge networking options
-func validateBridgeOptions(options entities.NetworkCreateOptions) error {
- subnet := &options.Subnet
- ipRange := &options.Range
- gateway := options.Gateway
- // if IPv6 is set an IPv6 subnet MUST be specified
- if options.IPv6 && ((subnet.IP == nil) || (subnet.IP != nil && !IsIPv6(subnet.IP))) {
- return errors.Errorf("ipv6 option requires an IPv6 --subnet to be provided")
- }
- // range and gateway depend on subnet
- if subnet.IP == nil && (ipRange.IP != nil || gateway != nil) {
- return errors.Errorf("every ip-range or gateway must have a corresponding subnet")
- }
-
- // if a range is given, we need to ensure it is "in" the network range.
- if ipRange.IP != nil {
- firstIP, err := FirstIPInSubnet(ipRange)
- if err != nil {
- return errors.Wrapf(err, "failed to get first IP address from ip-range")
- }
- lastIP, err := LastIPInSubnet(ipRange)
- if err != nil {
- return errors.Wrapf(err, "failed to get last IP address from ip-range")
- }
- if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) {
- return errors.Errorf("the ip range %s does not fall within the subnet range %s", ipRange.String(), subnet.String())
- }
- }
-
- // if network is provided and if gateway is provided, make sure it is "in" network
- if gateway != nil && !subnet.Contains(gateway) {
- return errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String())
- }
-
- return nil
-}
-
-// parseMTU parses the mtu option
-func parseMTU(mtu string) (int, error) {
- if mtu == "" {
- return 0, nil // default
- }
- m, err := strconv.Atoi(mtu)
- if err != nil {
- return 0, err
- }
- if m < 0 {
- return 0, errors.Errorf("the value %d for mtu is less than zero", m)
- }
- return m, nil
-}
-
-// parseVlan parses the vlan option
-func parseVlan(vlan string) (int, error) {
- if vlan == "" {
- return 0, nil // default
- }
- return strconv.Atoi(vlan)
-}
-
-// createBridge creates a CNI network
-func createBridge(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) {
- var (
- ipamRanges [][]IPAMLocalHostRangeConf
- err error
- routes []IPAMRoute
- )
- isGateway := true
- ipMasq := true
-
- // validate options
- if err := validateBridgeOptions(options); err != nil {
- return "", err
- }
-
- // For compatibility with the docker implementation:
- // if IPv6 is enabled (it really means dual-stack) then an IPv6 subnet has to be provided, and one free network is allocated for IPv4
- // if IPv6 is not specified the subnet may be specified and can be either IPv4 or IPv6 (podman, unlike docker, allows IPv6 only networks)
- // If not subnet is specified an IPv4 subnet will be allocated
- subnet := &options.Subnet
- ipRange := &options.Range
- gateway := options.Gateway
- if subnet.IP != nil {
- // if network is provided, does it conflict with existing CNI or live networks
- err = ValidateUserNetworkIsAvailable(runtimeConfig, subnet)
- if err != nil {
- return "", err
- }
- // obtain CNI subnet default route
- defaultRoute, err := NewIPAMDefaultRoute(IsIPv6(subnet.IP))
- if err != nil {
- return "", err
- }
- routes = append(routes, defaultRoute)
- // obtain CNI range
- ipamRange, err := NewIPAMLocalHostRange(subnet, ipRange, gateway)
- if err != nil {
- return "", err
- }
- ipamRanges = append(ipamRanges, ipamRange)
- }
- // if no network is provided or IPv6 flag used, figure out the IPv4 network
- if options.IPv6 || len(routes) == 0 {
- subnetV4, err := GetFreeNetwork(runtimeConfig)
- if err != nil {
- return "", err
- }
- // obtain IPv4 default route
- defaultRoute, err := NewIPAMDefaultRoute(false)
- if err != nil {
- return "", err
- }
- routes = append(routes, defaultRoute)
- // the CNI bridge plugin does not need to set
- // the range or gateway options explicitly
- ipamRange, err := NewIPAMLocalHostRange(subnetV4, nil, nil)
- if err != nil {
- return "", err
- }
- ipamRanges = append(ipamRanges, ipamRange)
- }
-
- // create CNI config
- ipamConfig, err := NewIPAMHostLocalConf(routes, ipamRanges)
- if err != nil {
- return "", err
- }
-
- if options.Internal {
- isGateway = false
- ipMasq = false
- }
-
- var mtu int
- var vlan int
- for k, v := range options.Options {
- var err error
- switch k {
- case "mtu":
- mtu, err = parseMTU(v)
- if err != nil {
- return "", err
- }
-
- case "vlan":
- vlan, err = parseVlan(v)
- if err != nil {
- return "", err
- }
-
- default:
- return "", errors.Errorf("unsupported option %s", k)
- }
- }
-
- // obtain host bridge name
- bridgeDeviceName, err := GetFreeDeviceName(runtimeConfig)
- if err != nil {
- return "", err
- }
-
- if len(name) > 0 {
- netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig)
- if err != nil {
- return "", err
- }
- if util.StringInSlice(name, netNames) {
- return "", errors.Errorf("the network name %s is already used", name)
- }
- } else {
- // If no name is given, we give the name of the bridge device
- name = bridgeDeviceName
- }
-
- // create CNI plugin configuration
- ncList := NewNcList(name, version.Current(), options.Labels)
- var plugins []CNIPlugins
- // TODO need to iron out the role of isDefaultGW and IPMasq
- bridge := NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, mtu, vlan, ipamConfig)
- plugins = append(plugins, bridge)
- plugins = append(plugins, NewPortMapPlugin())
- plugins = append(plugins, NewFirewallPlugin())
- plugins = append(plugins, NewTuningPlugin())
- // if we find the dnsname plugin we add configuration for it
- if HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) && !options.DisableDNS {
- if options.Internal {
- logrus.Warnf("dnsname and --internal networks are incompatible. dnsname plugin not configured for network %s", name)
- } else {
- // Note: in the future we might like to allow for dynamic domain names
- plugins = append(plugins, NewDNSNamePlugin(DefaultPodmanDomainName))
- }
- }
- // Add the podman-machine CNI plugin if we are in a machine
- if runtimeConfig.MachineEnabled() { // check if we are in a machine vm
- plugins = append(plugins, NewPodmanMachinePlugin())
- }
- ncList["plugins"] = plugins
- b, err := json.MarshalIndent(ncList, "", " ")
- if err != nil {
- return "", err
- }
- if err := os.MkdirAll(GetCNIConfDir(runtimeConfig), 0755); err != nil {
- return "", err
- }
- cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name))
- err = ioutil.WriteFile(cniPathName, b, 0644)
- return cniPathName, err
-}
-
-func createMacVLAN(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) {
- var (
- mtu int
- plugins []CNIPlugins
- )
- liveNetNames, err := GetLiveNetworkNames()
- if err != nil {
- return "", err
- }
-
- // The parent can be defined with --macvlan or as an option (-o parent:device)
- parentNetworkDevice := options.MacVLAN
- if len(parentNetworkDevice) < 1 {
- if parent, ok := options.Options["parent"]; ok {
- parentNetworkDevice = parent
- }
- }
-
- // Make sure the host-device exists if provided
- if len(parentNetworkDevice) > 0 && !util.StringInSlice(parentNetworkDevice, liveNetNames) {
- return "", errors.Errorf("failed to find network interface %q", parentNetworkDevice)
- }
- if len(name) > 0 {
- netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig)
- if err != nil {
- return "", err
- }
- if util.StringInSlice(name, netNames) {
- return "", errors.Errorf("the network name %s is already used", name)
- }
- } else {
- name, err = GetFreeDeviceName(runtimeConfig)
- if err != nil {
- return "", err
- }
- }
- ncList := NewNcList(name, version.Current(), options.Labels)
- if val, ok := options.Options["mtu"]; ok {
- intVal, err := strconv.Atoi(val)
- if err != nil {
- return "", err
- }
- if intVal > 0 {
- mtu = intVal
- }
- }
- macvlan, err := NewMacVLANPlugin(parentNetworkDevice, options.Gateway, &options.Range, &options.Subnet, mtu)
- if err != nil {
- return "", err
- }
- plugins = append(plugins, macvlan)
- ncList["plugins"] = plugins
- b, err := json.MarshalIndent(ncList, "", " ")
- if err != nil {
- return "", err
- }
- cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name))
- err = ioutil.WriteFile(cniPathName, b, 0644)
- return cniPathName, err
-}
diff --git a/libpod/network/create_test.go b/libpod/network/create_test.go
deleted file mode 100644
index c3824bd91..000000000
--- a/libpod/network/create_test.go
+++ /dev/null
@@ -1,130 +0,0 @@
-package network
-
-import (
- "net"
- "testing"
-
- "github.com/containers/podman/v3/pkg/domain/entities"
-)
-
-func Test_validateBridgeOptions(t *testing.T) {
- tests := []struct {
- name string
- subnet net.IPNet
- ipRange net.IPNet
- gateway net.IP
- isIPv6 bool
- wantErr bool
- }{
- {
- name: "IPv4 subnet only",
- subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- },
- {
- name: "IPv4 subnet and range",
- subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
- },
- {
- name: "IPv4 subnet and gateway",
- subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- gateway: net.ParseIP("192.168.0.10"),
- },
- {
- name: "IPv4 subnet, range and gateway",
- subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
- gateway: net.ParseIP("192.168.0.10"),
- },
- {
- name: "IPv6 subnet only",
- subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- },
- {
- name: "IPv6 subnet and range",
- subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- ipRange: net.IPNet{IP: net.ParseIP("2001:DB8:0:0:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))},
- isIPv6: true,
- },
- {
- name: "IPv6 subnet and gateway",
- subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- gateway: net.ParseIP("2001:DB8::2"),
- isIPv6: true,
- },
- {
- name: "IPv6 subnet, range and gateway",
- subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- ipRange: net.IPNet{IP: net.ParseIP("2001:DB8:0:0:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))},
- gateway: net.ParseIP("2001:DB8::2"),
- isIPv6: true,
- },
- {
- name: "IPv6 subnet, range and gateway without IPv6 option (PODMAN SUPPORTS IT UNLIKE DOCKER)",
- subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- ipRange: net.IPNet{IP: net.ParseIP("2001:DB8:0:0:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))},
- gateway: net.ParseIP("2001:DB8::2"),
- isIPv6: false,
- },
- {
- name: "range provided but not subnet",
- ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
- wantErr: true,
- },
- {
- name: "gateway provided but not subnet",
- gateway: net.ParseIP("192.168.0.10"),
- wantErr: true,
- },
- {
- name: "IPv4 subnet but IPv6 required",
- subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
- gateway: net.ParseIP("192.168.0.10"),
- isIPv6: true,
- wantErr: true,
- },
- {
- name: "IPv6 required but IPv4 options used",
- subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
- gateway: net.ParseIP("192.168.0.10"),
- isIPv6: true,
- wantErr: true,
- },
- {
- name: "IPv6 required but not subnet provided",
- isIPv6: true,
- wantErr: true,
- },
- {
- name: "range out of the subnet",
- subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- ipRange: net.IPNet{IP: net.ParseIP("2001:1:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))},
- gateway: net.ParseIP("2001:DB8::2"),
- isIPv6: true,
- wantErr: true,
- },
- {
- name: "gateway out of the subnet",
- subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- gateway: net.ParseIP("2001::2"),
- isIPv6: true,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- tt := tt
- t.Run(tt.name, func(t *testing.T) {
- options := entities.NetworkCreateOptions{
- Subnet: tt.subnet,
- Range: tt.ipRange,
- Gateway: tt.gateway,
- IPv6: tt.isIPv6,
- }
- if err := validateBridgeOptions(options); (err != nil) != tt.wantErr {
- t.Errorf("validateBridgeOptions() error = %v, wantErr %v", err, tt.wantErr)
- }
- })
- }
-}
diff --git a/libpod/network/devices.go b/libpod/network/devices.go
deleted file mode 100644
index fc9aff337..000000000
--- a/libpod/network/devices.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package network
-
-import (
- "fmt"
-
- "github.com/containers/common/pkg/config"
- "github.com/containers/podman/v3/pkg/util"
- "github.com/sirupsen/logrus"
- "github.com/vishvananda/netlink"
-)
-
-// GetFreeDeviceName returns a device name that is unused; used when no network
-// name is provided by user
-func GetFreeDeviceName(config *config.Config) (string, error) {
- var (
- deviceNum uint
- deviceName string
- )
- networkNames, err := GetNetworkNamesFromFileSystem(config)
- if err != nil {
- return "", err
- }
- liveNetworksNames, err := GetLiveNetworkNames()
- if err != nil {
- return "", err
- }
- bridgeNames, err := GetBridgeNamesFromFileSystem(config)
- if err != nil {
- return "", err
- }
- for {
- deviceName = fmt.Sprintf("%s%d", CNIDeviceName, deviceNum)
- logrus.Debugf("checking if device name %q exists in other cni networks", deviceName)
- if util.StringInSlice(deviceName, networkNames) {
- deviceNum++
- continue
- }
- logrus.Debugf("checking if device name %q exists in live networks", deviceName)
- if util.StringInSlice(deviceName, liveNetworksNames) {
- deviceNum++
- continue
- }
- logrus.Debugf("checking if device name %q already exists as a bridge name ", deviceName)
- if !util.StringInSlice(deviceName, bridgeNames) {
- break
- }
- deviceNum++
- }
- return deviceName, nil
-}
-
-// RemoveInterface removes an interface by the given name
-func RemoveInterface(interfaceName string) error {
- link, err := netlink.LinkByName(interfaceName)
- if err != nil {
- return err
- }
- return netlink.LinkDel(link)
-}
diff --git a/libpod/network/files.go b/libpod/network/files.go
deleted file mode 100644
index d876113f9..000000000
--- a/libpod/network/files.go
+++ /dev/null
@@ -1,211 +0,0 @@
-package network
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "sort"
- "strings"
-
- "github.com/containernetworking/cni/libcni"
- "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
- "github.com/containers/common/pkg/config"
- "github.com/containers/podman/v3/libpod/define"
- "github.com/containers/podman/v3/pkg/network"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-// ErrNoSuchNetworkInterface indicates that no network interface exists
-var ErrNoSuchNetworkInterface = errors.New("unable to find interface name for network")
-
-// GetCNIConfDir get CNI configuration directory
-func GetCNIConfDir(configArg *config.Config) string {
- if len(configArg.Network.NetworkConfigDir) < 1 {
- dc, err := config.DefaultConfig()
- if err != nil {
- // Fallback to hard-coded dir
- return CNIConfigDir
- }
- return dc.Network.NetworkConfigDir
- }
- return configArg.Network.NetworkConfigDir
-}
-
-// LoadCNIConfsFromDir loads all the CNI configurations from a dir
-func LoadCNIConfsFromDir(dir string) ([]*libcni.NetworkConfigList, error) {
- files, err := libcni.ConfFiles(dir, []string{".conflist"})
- if err != nil {
- return nil, err
- }
- sort.Strings(files)
-
- configs := make([]*libcni.NetworkConfigList, 0, len(files))
- for _, confFile := range files {
- conf, err := libcni.ConfListFromFile(confFile)
- if err != nil {
- return nil, errors.Wrapf(err, "in %s", confFile)
- }
- configs = append(configs, conf)
- }
- return configs, nil
-}
-
-// GetCNIConfigPathByNameOrID finds a CNI network by name and
-// returns its configuration file path
-func GetCNIConfigPathByNameOrID(config *config.Config, name string) (string, error) {
- files, err := libcni.ConfFiles(GetCNIConfDir(config), []string{".conflist"})
- if err != nil {
- return "", err
- }
- idMatch := 0
- file := ""
- for _, confFile := range files {
- conf, err := libcni.ConfListFromFile(confFile)
- if err != nil {
- return "", errors.Wrapf(err, "in %s", confFile)
- }
- if conf.Name == name {
- return confFile, nil
- }
- if strings.HasPrefix(network.GetNetworkID(conf.Name), name) {
- idMatch++
- file = confFile
- }
- }
- if idMatch == 1 {
- return file, nil
- }
- if idMatch > 1 {
- return "", errors.Errorf("more than one result for network ID %s", name)
- }
- return "", errors.Wrap(define.ErrNoSuchNetwork, fmt.Sprintf("unable to find network configuration for %s", name))
-}
-
-// ReadRawCNIConfByNameOrID reads the raw CNI configuration for a CNI
-// network by name
-func ReadRawCNIConfByNameOrID(config *config.Config, name string) ([]byte, error) {
- confFile, err := GetCNIConfigPathByNameOrID(config, name)
- if err != nil {
- return nil, err
- }
- b, err := ioutil.ReadFile(confFile)
- return b, err
-}
-
-// GetNetworkLabels returns a list of labels as a string
-func GetNetworkLabels(list *libcni.NetworkConfigList) NcLabels {
- cniJSON := make(map[string]interface{})
- err := json.Unmarshal(list.Bytes, &cniJSON)
- if err != nil {
- logrus.Errorf("failed to unmarshal network config %v %v", cniJSON["name"], err)
- return nil
- }
- if args, ok := cniJSON["args"]; ok {
- if key, ok := args.(map[string]interface{}); ok {
- if labels, ok := key[PodmanLabelKey]; ok {
- if labels, ok := labels.(map[string]interface{}); ok {
- result := make(NcLabels, len(labels))
- for k, v := range labels {
- if v, ok := v.(string); ok {
- result[k] = v
- } else {
- logrus.Errorf("network config %v invalid label value type %T should be string", cniJSON["name"], labels)
- }
- }
- return result
- }
- logrus.Errorf("network config %v invalid label type %T should be map[string]string", cniJSON["name"], labels)
- }
- }
- }
- return nil
-}
-
-// GetNetworksFromFilesystem gets all the networks from the cni configuration
-// files
-func GetNetworksFromFilesystem(config *config.Config) ([]*allocator.Net, error) {
- var cniNetworks []*allocator.Net
-
- networks, err := LoadCNIConfsFromDir(GetCNIConfDir(config))
- if err != nil {
- return nil, err
- }
- for _, n := range networks {
- for _, cniplugin := range n.Plugins {
- if cniplugin.Network.Type == "bridge" {
- ipamConf := allocator.Net{}
- if err := json.Unmarshal(cniplugin.Bytes, &ipamConf); err != nil {
- return nil, err
- }
- cniNetworks = append(cniNetworks, &ipamConf)
- break
- }
- }
- }
- return cniNetworks, nil
-}
-
-// GetNetworkNamesFromFileSystem gets all the names from the cni network
-// configuration files
-func GetNetworkNamesFromFileSystem(config *config.Config) ([]string, error) {
- networks, err := LoadCNIConfsFromDir(GetCNIConfDir(config))
- if err != nil {
- return nil, err
- }
- networkNames := []string{}
- for _, n := range networks {
- networkNames = append(networkNames, n.Name)
- }
- return networkNames, nil
-}
-
-// GetInterfaceNameFromConfig returns the interface name for the bridge plugin
-func GetInterfaceNameFromConfig(path string) (string, error) {
- var name string
- conf, err := libcni.ConfListFromFile(path)
- if err != nil {
- return "", err
- }
- for _, cniplugin := range conf.Plugins {
- if cniplugin.Network.Type == "bridge" {
- plugin := make(map[string]interface{})
- if err := json.Unmarshal(cniplugin.Bytes, &plugin); err != nil {
- return "", err
- }
- name = plugin["bridge"].(string)
- break
- }
- }
- if len(name) == 0 {
- return "", ErrNoSuchNetworkInterface
- }
- return name, nil
-}
-
-// GetBridgeNamesFromFileSystem is a convenience function to get all the bridge
-// names from the configured networks
-func GetBridgeNamesFromFileSystem(config *config.Config) ([]string, error) {
- networks, err := LoadCNIConfsFromDir(GetCNIConfDir(config))
- if err != nil {
- return nil, err
- }
-
- bridgeNames := []string{}
- for _, n := range networks {
- var name string
- // iterate network conflists
- for _, cniplugin := range n.Plugins {
- // iterate plugins
- if cniplugin.Network.Type == "bridge" {
- plugin := make(map[string]interface{})
- if err := json.Unmarshal(cniplugin.Bytes, &plugin); err != nil {
- continue
- }
- name = plugin["bridge"].(string)
- }
- }
- bridgeNames = append(bridgeNames, name)
- }
- return bridgeNames, nil
-}
diff --git a/libpod/network/ip.go b/libpod/network/ip.go
deleted file mode 100644
index ba93a0d05..000000000
--- a/libpod/network/ip.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package network
-
-import (
- "net"
-
- "github.com/containernetworking/plugins/pkg/ip"
-)
-
-// CalcGatewayIP takes a network and returns the first IP in it.
-func CalcGatewayIP(ipn *net.IPNet) net.IP {
- // taken from cni bridge plugin as it is not exported
- nid := ipn.IP.Mask(ipn.Mask)
- return ip.NextIP(nid)
-}
-
-// IsIPv6 returns if netIP is IPv6.
-func IsIPv6(netIP net.IP) bool {
- return netIP != nil && netIP.To4() == nil
-}
diff --git a/libpod/network/lock.go b/libpod/network/lock.go
deleted file mode 100644
index 037f41efa..000000000
--- a/libpod/network/lock.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package network
-
-import (
- "os"
- "path/filepath"
-
- "github.com/containers/common/pkg/config"
- "github.com/containers/storage"
-)
-
-// acquireCNILock gets a lock that should be used in create and
-// delete cases to avoid unwanted collisions in network names.
-// TODO this uses a file lock and should be converted to shared memory
-// when we have a more general shared memory lock in libpod
-func acquireCNILock(config *config.Config) (*CNILock, error) {
- cniDir := GetCNIConfDir(config)
- err := os.MkdirAll(cniDir, 0755)
- if err != nil {
- return nil, err
- }
- l, err := storage.GetLockfile(filepath.Join(cniDir, LockFileName))
- if err != nil {
- return nil, err
- }
- l.Lock()
- cnilock := CNILock{
- Locker: l,
- }
- return &cnilock, nil
-}
-
-// ReleaseCNILock unlocks the previously held lock
-func (l *CNILock) releaseCNILock() {
- l.Unlock()
-}
diff --git a/libpod/network/netconflist.go b/libpod/network/netconflist.go
deleted file mode 100644
index d6c33740e..000000000
--- a/libpod/network/netconflist.go
+++ /dev/null
@@ -1,312 +0,0 @@
-package network
-
-import (
- "net"
- "os"
- "path/filepath"
- "strings"
- "syscall"
- "time"
-
- "github.com/containernetworking/cni/libcni"
- "github.com/containers/common/pkg/config"
- "github.com/containers/podman/v3/pkg/network"
- "github.com/containers/podman/v3/pkg/util"
- "github.com/pkg/errors"
-)
-
-const (
- defaultIPv4Route = "0.0.0.0/0"
- defaultIPv6Route = "::/0"
-)
-
-// NcList describes a generic map
-type NcList map[string]interface{}
-
-// NcArgs describes the cni args field
-type NcArgs map[string]NcLabels
-
-// NcLabels describes the label map
-type NcLabels map[string]string
-
-// PodmanLabelKey key used to store the podman network label in a cni config
-const PodmanLabelKey = "podman_labels"
-
-// NewNcList creates a generic map of values with string
-// keys and adds in version and network name
-func NewNcList(name, version string, labels NcLabels) NcList {
- n := NcList{}
- n["cniVersion"] = version
- n["name"] = name
- if len(labels) > 0 {
- n["args"] = NcArgs{PodmanLabelKey: labels}
- }
- return n
-}
-
-// NewHostLocalBridge creates a new LocalBridge for host-local
-func NewHostLocalBridge(name string, isGateWay, isDefaultGW, ipMasq bool, mtu int, vlan int, ipamConf IPAMConfig) *HostLocalBridge {
- hostLocalBridge := HostLocalBridge{
- PluginType: "bridge",
- BrName: name,
- IPMasq: ipMasq,
- MTU: mtu,
- HairpinMode: true,
- Vlan: vlan,
- IPAM: ipamConf,
- }
- if isGateWay {
- hostLocalBridge.IsGW = true
- }
- if isDefaultGW {
- hostLocalBridge.IsDefaultGW = true
- }
- return &hostLocalBridge
-}
-
-// NewIPAMHostLocalConf creates a new IPAMHostLocal configuration
-func NewIPAMHostLocalConf(routes []IPAMRoute, ipamRanges [][]IPAMLocalHostRangeConf) (IPAMConfig, error) {
- ipamConf := IPAMConfig{
- PluginType: "host-local",
- Routes: routes,
- // Possible future support ? Leaving for clues
- //ResolveConf: "",
- //DataDir: ""
- }
-
- ipamConf.Ranges = ipamRanges
- return ipamConf, nil
-}
-
-// NewIPAMLocalHostRange create a new IPAM range
-func NewIPAMLocalHostRange(subnet *net.IPNet, ipRange *net.IPNet, gw net.IP) ([]IPAMLocalHostRangeConf, error) { //nolint:interfacer
- var ranges []IPAMLocalHostRangeConf
- hostRange := IPAMLocalHostRangeConf{
- Subnet: subnet.String(),
- }
- // an user provided a range, we add it here
- if ipRange != nil && ipRange.IP != nil {
- first, err := FirstIPInSubnet(ipRange)
- if err != nil {
- return nil, err
- }
- last, err := LastIPInSubnet(ipRange)
- if err != nil {
- return nil, err
- }
- hostRange.RangeStart = first.String()
- hostRange.RangeEnd = last.String()
- }
- if gw != nil {
- hostRange.Gateway = gw.String()
- } else {
- // Add first ip in subnet as gateway. It is not required
- // by cni but should be included because of network inspect.
- hostRange.Gateway = CalcGatewayIP(subnet).String()
- }
- ranges = append(ranges, hostRange)
- return ranges, nil
-}
-
-// NewIPAMRoute creates a new IPAM route configuration
-func NewIPAMRoute(r *net.IPNet) IPAMRoute { //nolint:interfacer
- return IPAMRoute{Dest: r.String()}
-}
-
-// NewIPAMDefaultRoute creates a new IPAMDefault route of
-// 0.0.0.0/0 for IPv4 or ::/0 for IPv6
-func NewIPAMDefaultRoute(isIPv6 bool) (IPAMRoute, error) {
- route := defaultIPv4Route
- if isIPv6 {
- route = defaultIPv6Route
- }
- _, n, err := net.ParseCIDR(route)
- if err != nil {
- return IPAMRoute{}, err
- }
- return NewIPAMRoute(n), nil
-}
-
-// NewPortMapPlugin creates a predefined, default portmapping
-// configuration
-func NewPortMapPlugin() PortMapConfig {
- caps := make(map[string]bool)
- caps["portMappings"] = true
- p := PortMapConfig{
- PluginType: "portmap",
- Capabilities: caps,
- }
- return p
-}
-
-// NewFirewallPlugin creates a generic firewall plugin
-func NewFirewallPlugin() FirewallConfig {
- return FirewallConfig{
- PluginType: "firewall",
- }
-}
-
-// NewTuningPlugin creates a generic tuning section
-func NewTuningPlugin() TuningConfig {
- return TuningConfig{
- PluginType: "tuning",
- }
-}
-
-// NewDNSNamePlugin creates the dnsname config with a given
-// domainname
-func NewDNSNamePlugin(domainName string) DNSNameConfig {
- caps := make(map[string]bool, 1)
- caps["aliases"] = true
- return DNSNameConfig{
- PluginType: "dnsname",
- DomainName: domainName,
- Capabilities: caps,
- }
-}
-
-// HasDNSNamePlugin looks to see if the dnsname cni plugin is present
-func HasDNSNamePlugin(paths []string) bool {
- for _, p := range paths {
- if _, err := os.Stat(filepath.Join(p, "dnsname")); err == nil {
- return true
- }
- }
- return false
-}
-
-// NewMacVLANPlugin creates a macvlanconfig with a given device name
-func NewMacVLANPlugin(device string, gateway net.IP, ipRange *net.IPNet, subnet *net.IPNet, mtu int) (MacVLANConfig, error) {
- i := IPAMConfig{PluginType: "dhcp"}
- if gateway != nil ||
- (ipRange != nil && ipRange.IP != nil && ipRange.Mask != nil) ||
- (subnet != nil && subnet.IP != nil && subnet.Mask != nil) {
- ipam, err := NewIPAMLocalHostRange(subnet, ipRange, gateway)
- if err != nil {
- return MacVLANConfig{}, err
- }
- ranges := make([][]IPAMLocalHostRangeConf, 0)
- ranges = append(ranges, ipam)
- i.Ranges = ranges
- route, err := NewIPAMDefaultRoute(IsIPv6(subnet.IP))
- if err != nil {
- return MacVLANConfig{}, err
- }
- i.Routes = []IPAMRoute{route}
- i.PluginType = "host-local"
- }
-
- m := MacVLANConfig{
- PluginType: "macvlan",
- IPAM: i,
- }
- if mtu > 0 {
- m.MTU = mtu
- }
- // CNI is supposed to use the default route if a
- // parent device is not provided
- if len(device) > 0 {
- m.Master = device
- }
- return m, nil
-}
-
-// IfPassesFilter filters NetworkListReport and returns true if the filter match the given config
-func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]string) (bool, error) {
- result := true
- for key, filterValues := range filters {
- result = false
- switch strings.ToLower(key) {
- case "name":
- // matches one name, regex allowed
- result = util.StringMatchRegexSlice(netconf.Name, filterValues)
-
- case "plugin":
- // match one plugin
- plugins := network.GetCNIPlugins(netconf)
- for _, val := range filterValues {
- if strings.Contains(plugins, val) {
- result = true
- break
- }
- }
-
- case "label":
- // matches all labels
- result = util.MatchLabelFilters(filterValues, GetNetworkLabels(netconf))
-
- case "driver":
- // matches only for the DefaultNetworkDriver
- for _, filterValue := range filterValues {
- plugins := network.GetCNIPlugins(netconf)
- if filterValue == DefaultNetworkDriver &&
- strings.Contains(plugins, DefaultNetworkDriver) {
- result = true
- }
- }
-
- case "id":
- // matches part of one id
- for _, filterValue := range filterValues {
- if strings.Contains(network.GetNetworkID(netconf.Name), filterValue) {
- result = true
- break
- }
- }
-
- // TODO: add dangling filter
-
- default:
- return false, errors.Errorf("invalid filter %q", key)
- }
- }
- return result, nil
-}
-
-// IfPassesPruneFilter filters NetworkListReport and returns true if the prune filter match the given config
-func IfPassesPruneFilter(config *config.Config, netconf *libcni.NetworkConfigList, f map[string][]string) (bool, error) {
- for key, filterValues := range f {
- switch strings.ToLower(key) {
- case "label":
- return util.MatchLabelFilters(filterValues, GetNetworkLabels(netconf)), nil
- case "until":
- until, err := util.ComputeUntilTimestamp(filterValues)
- if err != nil {
- return false, err
- }
- created, err := getCreatedTimestamp(config, netconf)
- if err != nil {
- return false, err
- }
- if created.Before(until) {
- return true, nil
- }
- default:
- return false, errors.Errorf("invalid filter %q", key)
- }
- }
- return false, nil
-}
-
-func getCreatedTimestamp(config *config.Config, netconf *libcni.NetworkConfigList) (*time.Time, error) {
- networkConfigPath, err := GetCNIConfigPathByNameOrID(config, netconf.Name)
- if err != nil {
- return nil, err
- }
- f, err := os.Stat(networkConfigPath)
- if err != nil {
- return nil, err
- }
- stat := f.Sys().(*syscall.Stat_t)
- created := time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)) // nolint: unconvert
- return &created, nil
-}
-
-func NewPodmanMachinePlugin() PodmanMachineConfig {
- caps := make(map[string]bool, 1)
- caps["portMappings"] = true
- return PodmanMachineConfig{
- PluginType: "podman-machine",
- Capabilities: caps,
- }
-}
diff --git a/libpod/network/netconflist_test.go b/libpod/network/netconflist_test.go
deleted file mode 100644
index 161764ed9..000000000
--- a/libpod/network/netconflist_test.go
+++ /dev/null
@@ -1,109 +0,0 @@
-package network
-
-import (
- "net"
- "reflect"
- "testing"
-)
-
-func TestNewIPAMDefaultRoute(t *testing.T) {
- tests := []struct {
- name string
- isIPv6 bool
- want IPAMRoute
- }{
- {
- name: "IPv4 default route",
- isIPv6: false,
- want: IPAMRoute{defaultIPv4Route},
- },
- {
- name: "IPv6 default route",
- isIPv6: true,
- want: IPAMRoute{defaultIPv6Route},
- },
- }
- for _, tt := range tests {
- tt := tt
- t.Run(tt.name, func(t *testing.T) {
- got, err := NewIPAMDefaultRoute(tt.isIPv6)
- if err != nil {
- t.Errorf("no error expected: %v", err)
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("NewIPAMDefaultRoute() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestNewIPAMLocalHostRange(t *testing.T) {
- tests := []struct {
- name string
- subnet *net.IPNet
- ipRange *net.IPNet
- gw net.IP
- want []IPAMLocalHostRangeConf
- }{
- {
- name: "IPv4 subnet",
- subnet: &net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- want: []IPAMLocalHostRangeConf{
- {
- Subnet: "192.168.0.0/24",
- Gateway: "192.168.0.1",
- },
- },
- },
- {
- name: "IPv4 subnet, range and gateway",
- subnet: &net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)},
- ipRange: &net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)},
- gw: net.ParseIP("192.168.0.10"),
- want: []IPAMLocalHostRangeConf{
- {
- Subnet: "192.168.0.0/24",
- RangeStart: "192.168.0.129",
- RangeEnd: "192.168.0.255",
- Gateway: "192.168.0.10",
- },
- },
- },
- {
- name: "IPv6 subnet",
- subnet: &net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- want: []IPAMLocalHostRangeConf{
- {
- Subnet: "2001:db8::/48",
- Gateway: "2001:db8::1",
- },
- },
- },
- {
- name: "IPv6 subnet, range and gateway",
- subnet: &net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))},
- ipRange: &net.IPNet{IP: net.ParseIP("2001:DB8:1:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))},
- gw: net.ParseIP("2001:DB8::2"),
- want: []IPAMLocalHostRangeConf{
- {
- Subnet: "2001:db8::/48",
- RangeStart: "2001:db8:1:1::1",
- RangeEnd: "2001:db8:1:1:ffff:ffff:ffff:ffff",
- Gateway: "2001:db8::2",
- },
- },
- },
- }
- for _, tt := range tests {
- tt := tt
- t.Run(tt.name, func(t *testing.T) {
- got, err := NewIPAMLocalHostRange(tt.subnet, tt.ipRange, tt.gw)
- if err != nil {
- t.Errorf("no error expected: %v", err)
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("NewIPAMLocalHostRange() = %v, want %v", got, tt.want)
- }
- })
- }
-}
diff --git a/libpod/network/network.go b/libpod/network/network.go
deleted file mode 100644
index 3b81ce776..000000000
--- a/libpod/network/network.go
+++ /dev/null
@@ -1,288 +0,0 @@
-package network
-
-import (
- "encoding/json"
- "net"
- "os"
-
- "github.com/containernetworking/cni/libcni"
- "github.com/containernetworking/cni/pkg/types"
- "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
- "github.com/containers/common/pkg/config"
- "github.com/containers/podman/v3/libpod/define"
- "github.com/containers/podman/v3/pkg/domain/entities"
- "github.com/containers/podman/v3/pkg/rootless"
- "github.com/containers/podman/v3/pkg/util"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-var (
- // BridgeNetworkDriver defines the bridge cni driver
- BridgeNetworkDriver = "bridge"
- // DefaultNetworkDriver is the default network type used
- DefaultNetworkDriver = BridgeNetworkDriver
- // MacVLANNetworkDriver defines the macvlan cni driver
- MacVLANNetworkDriver = "macvlan"
-)
-
-// SupportedNetworkDrivers describes the list of supported drivers
-var SupportedNetworkDrivers = []string{BridgeNetworkDriver, MacVLANNetworkDriver}
-
-// isSupportedDriver checks if the user provided driver is supported
-func isSupportedDriver(driver string) error {
- if util.StringInSlice(driver, SupportedNetworkDrivers) {
- return nil
- }
- return errors.Errorf("driver '%s' is not supported", driver)
-}
-
-// GetLiveNetworks returns a slice of networks representing what the system
-// has defined as network interfaces
-func GetLiveNetworks() ([]*net.IPNet, error) {
- addrs, err := net.InterfaceAddrs()
- if err != nil {
- return nil, err
- }
- nets := make([]*net.IPNet, 0, len(addrs))
- for _, address := range addrs {
- _, n, err := net.ParseCIDR(address.String())
- if err != nil {
- return nil, err
- }
- nets = append(nets, n)
- }
- return nets, nil
-}
-
-// GetLiveNetworkNames returns a list of network interfaces on the system
-func GetLiveNetworkNames() ([]string, error) {
- liveInterfaces, err := net.Interfaces()
- if err != nil {
- return nil, err
- }
- interfaceNames := make([]string, 0, len(liveInterfaces))
- for _, i := range liveInterfaces {
- interfaceNames = append(interfaceNames, i.Name)
- }
- return interfaceNames, nil
-}
-
-// GetFreeNetwork looks for a free network according to existing cni configuration
-// files and network interfaces.
-func GetFreeNetwork(config *config.Config) (*net.IPNet, error) {
- networks, err := GetNetworksFromFilesystem(config)
- if err != nil {
- return nil, err
- }
- liveNetworks, err := GetLiveNetworks()
- if err != nil {
- return nil, err
- }
- nextNetwork, err := GetDefaultPodmanNetwork()
- if err != nil {
- return nil, err
- }
- logrus.Debugf("default network is %s", nextNetwork.String())
- for {
- newNetwork, err := NextSubnet(nextNetwork)
- if err != nil {
- return nil, err
- }
- logrus.Debugf("checking if network %s intersects with other cni networks", nextNetwork.String())
- if intersectsConfig, _ := networkIntersectsWithNetworks(newNetwork, allocatorToIPNets(networks)); intersectsConfig {
- logrus.Debugf("network %s is already being used by a cni configuration", nextNetwork.String())
- nextNetwork = newNetwork
- continue
- }
- logrus.Debugf("checking if network %s intersects with any network interfaces", nextNetwork.String())
- if intersectsLive, _ := networkIntersectsWithNetworks(newNetwork, liveNetworks); !intersectsLive {
- break
- }
- logrus.Debugf("network %s is being used by a network interface", nextNetwork.String())
- nextNetwork = newNetwork
- }
- return nextNetwork, nil
-}
-
-func allocatorToIPNets(networks []*allocator.Net) []*net.IPNet {
- var nets []*net.IPNet
- for _, network := range networks {
- if len(network.IPAM.Ranges) > 0 {
- // this is the new IPAM range style
- // append each subnet from ipam the rangeset
- for _, allocatorRange := range network.IPAM.Ranges {
- for _, r := range allocatorRange {
- nets = append(nets, newIPNetFromSubnet(r.Subnet))
- }
- }
- } else {
- // looks like the old, deprecated style
- nets = append(nets, newIPNetFromSubnet(network.IPAM.Subnet))
- }
- }
- return nets
-}
-
-func newIPNetFromSubnet(subnet types.IPNet) *net.IPNet {
- n := net.IPNet{
- IP: subnet.IP,
- Mask: subnet.Mask,
- }
- return &n
-}
-
-func networkIntersectsWithNetworks(n *net.IPNet, networklist []*net.IPNet) (bool, *net.IPNet) {
- for _, nw := range networklist {
- if networkIntersect(n, nw) {
- return true, nw
- }
- }
- return false, nil
-}
-
-func networkIntersect(n1, n2 *net.IPNet) bool {
- return n2.Contains(n1.IP) || n1.Contains(n2.IP)
-}
-
-// ValidateUserNetworkIsAvailable returns via an error if a network is available
-// to be used
-func ValidateUserNetworkIsAvailable(config *config.Config, userNet *net.IPNet) error {
- if len(userNet.IP) == 0 || len(userNet.Mask) == 0 {
- return errors.Errorf("network %s's ip or mask cannot be empty", userNet.String())
- }
-
- ones, bit := userNet.Mask.Size()
- if ones == 0 || bit == 0 {
- return errors.Errorf("network %s's mask is invalid", userNet.String())
- }
-
- networks, err := GetNetworksFromFilesystem(config)
- if err != nil {
- return err
- }
- liveNetworks, err := GetLiveNetworks()
- if err != nil {
- return err
- }
- logrus.Debugf("checking if network %s exists in cni networks", userNet.String())
- if intersectsConfig, _ := networkIntersectsWithNetworks(userNet, allocatorToIPNets(networks)); intersectsConfig {
- return errors.Errorf("network %s is already being used by a cni configuration", userNet.String())
- }
- logrus.Debugf("checking if network %s exists in any network interfaces", userNet.String())
- if intersectsLive, _ := networkIntersectsWithNetworks(userNet, liveNetworks); intersectsLive {
- return errors.Errorf("network %s is being used by a network interface", userNet.String())
- }
- return nil
-}
-
-// removeNetwork is removes a cni network without a lock and should only be called
-// when a lock was otherwise acquired.
-func removeNetwork(config *config.Config, name string) error {
- cniPath, err := GetCNIConfigPathByNameOrID(config, name)
- if err != nil {
- return err
- }
- // Before we delete the configuration file, we need to make sure we can read and parse
- // it to get the network interface name so we can remove that too
- interfaceName, err := GetInterfaceNameFromConfig(cniPath)
- if err == nil {
- // Don't try to remove the network interface if we are not root
- if !rootless.IsRootless() {
- liveNetworkNames, err := GetLiveNetworkNames()
- if err != nil {
- return errors.Wrapf(err, "failed to get live network names")
- }
- if util.StringInSlice(interfaceName, liveNetworkNames) {
- if err = RemoveInterface(interfaceName); err != nil {
- // only log the error, it is not fatal
- logrus.Infof("failed to remove network interface %s: %v", interfaceName, err)
- }
- }
- }
- } else if err != ErrNoSuchNetworkInterface {
- // Don't error if we couldn't find the network interface name
- return err
- }
- // Remove the configuration file
- if err := os.Remove(cniPath); err != nil {
- return errors.Wrap(err, "failed to remove network configuration")
- }
- return nil
-}
-
-// RemoveNetwork removes a given network by name. If the network has container associated with it, that
-// must be handled outside the context of this.
-func RemoveNetwork(config *config.Config, name string) error {
- l, err := acquireCNILock(config)
- if err != nil {
- return err
- }
- defer l.releaseCNILock()
- return removeNetwork(config, name)
-}
-
-// InspectNetwork reads a CNI config and returns its configuration
-func InspectNetwork(config *config.Config, name string) (map[string]interface{}, error) {
- b, err := ReadRawCNIConfByNameOrID(config, name)
- if err != nil {
- return nil, err
- }
- rawList := make(map[string]interface{})
- err = json.Unmarshal(b, &rawList)
- return rawList, err
-}
-
-// Exists says whether a given network exists or not; it meant
-// specifically for restful responses so 404s can be used
-func Exists(config *config.Config, name string) (bool, error) {
- _, err := ReadRawCNIConfByNameOrID(config, name)
- if err != nil {
- if errors.Cause(err) == define.ErrNoSuchNetwork {
- return false, nil
- }
- return false, err
- }
- return true, nil
-}
-
-// PruneNetworks removes networks that are not being used and that is not the default
-// network. To keep proper fencing for imports, you must provide the used networks
-// to this function as a map. the key is meaningful in the map, the book is a no-op
-func PruneNetworks(rtc *config.Config, usedNetworks map[string]bool) ([]*entities.NetworkPruneReport, error) {
- var reports []*entities.NetworkPruneReport
- lock, err := acquireCNILock(rtc)
- if err != nil {
- return nil, err
- }
- defer lock.releaseCNILock()
- nets, err := GetNetworkNamesFromFileSystem(rtc)
- if err != nil {
- return nil, err
- }
- for _, n := range nets {
- _, found := usedNetworks[n]
- // Remove is not default network and not found in the used list
- if n != rtc.Network.DefaultNetwork && !found {
- reports = append(reports, &entities.NetworkPruneReport{
- Name: n,
- Error: removeNetwork(rtc, n),
- })
- }
- }
- return reports, nil
-}
-
-// NormalizeName translates a network ID into a name.
-// If the input is a name the name is returned.
-func NormalizeName(config *config.Config, nameOrID string) (string, error) {
- path, err := GetCNIConfigPathByNameOrID(config, nameOrID)
- if err != nil {
- return "", err
- }
- conf, err := libcni.ConfListFromFile(path)
- if err != nil {
- return "", err
- }
- return conf.Name, nil
-}
diff --git a/libpod/network/network_test.go b/libpod/network/network_test.go
deleted file mode 100644
index 1969e792c..000000000
--- a/libpod/network/network_test.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package network
-
-import (
- "net"
- "testing"
-)
-
-func parseCIDR(n string) *net.IPNet {
- _, parsedNet, _ := net.ParseCIDR(n)
- return parsedNet
-}
-
-func Test_networkIntersect(t *testing.T) {
- type args struct {
- n1 *net.IPNet
- n2 *net.IPNet
- }
- tests := []struct {
- name string
- args args
- want bool
- }{
- {"16 and 24 intersects", args{n1: parseCIDR("192.168.0.0/16"), n2: parseCIDR("192.168.1.0/24")}, true},
- {"24 and 25 intersects", args{n1: parseCIDR("192.168.1.0/24"), n2: parseCIDR("192.168.1.0/25")}, true},
- {"Two 24s", args{n1: parseCIDR("192.168.1.0/24"), n2: parseCIDR("192.168.2.0/24")}, false},
- }
- for _, tt := range tests {
- test := tt
- t.Run(tt.name, func(t *testing.T) {
- if got := networkIntersect(test.args.n1, test.args.n2); got != test.want {
- t.Errorf("networkIntersect() = %v, want %v", got, test.want)
- }
- })
- }
-}
diff --git a/libpod/network/subnet.go b/libpod/network/subnet.go
deleted file mode 100644
index 120038e57..000000000
--- a/libpod/network/subnet.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package network
-
-/*
- The code in this was kindly contributed by Dan Williams(dcbw@redhat.com). Many thanks
- for his contributions.
-*/
-
-import (
- "fmt"
- "net"
-)
-
-func incByte(subnet *net.IPNet, idx int, shift uint) error {
- if idx < 0 {
- return fmt.Errorf("no more subnets left")
- }
- if subnet.IP[idx] == 255 {
- subnet.IP[idx] = 0
- return incByte(subnet, idx-1, 0)
- }
- subnet.IP[idx] += 1 << shift
- return nil
-}
-
-// NextSubnet returns subnet incremented by 1
-func NextSubnet(subnet *net.IPNet) (*net.IPNet, error) {
- newSubnet := &net.IPNet{
- IP: subnet.IP,
- Mask: subnet.Mask,
- }
- ones, bits := newSubnet.Mask.Size()
- if ones == 0 {
- return nil, fmt.Errorf("%s has only one subnet", subnet.String())
- }
- zeroes := uint(bits - ones)
- shift := zeroes % 8
- idx := ones/8 - 1
- if idx < 0 {
- idx = 0
- }
- if err := incByte(newSubnet, idx, shift); err != nil {
- return nil, err
- }
- return newSubnet, nil
-}
-
-// LastIPInSubnet gets the last IP in a subnet
-func LastIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer
- // re-parse to ensure clean network address
- _, cidr, err := net.ParseCIDR(addr.String())
- if err != nil {
- return nil, err
- }
-
- ones, bits := cidr.Mask.Size()
- if ones == bits {
- return cidr.IP, nil
- }
- for i := range cidr.IP {
- cidr.IP[i] = cidr.IP[i] | ^cidr.Mask[i]
- }
- return cidr.IP, nil
-}
-
-// FirstIPInSubnet gets the first IP in a subnet
-func FirstIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer
- // re-parse to ensure clean network address
- _, cidr, err := net.ParseCIDR(addr.String())
- if err != nil {
- return nil, err
- }
- ones, bits := cidr.Mask.Size()
- if ones == bits {
- return cidr.IP, nil
- }
- cidr.IP[len(cidr.IP)-1]++
- return cidr.IP, nil
-}
diff --git a/libpod/network/subnet_test.go b/libpod/network/subnet_test.go
deleted file mode 100644
index 55b2443bd..000000000
--- a/libpod/network/subnet_test.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package network
-
-import (
- "net"
- "reflect"
- "testing"
-)
-
-func TestNextSubnet(t *testing.T) {
- type args struct {
- subnet *net.IPNet
- }
- tests := []struct {
- name string
- args args
- want *net.IPNet
- wantErr bool
- }{
- {"class b", args{subnet: parseCIDR("192.168.0.0/16")}, parseCIDR("192.169.0.0/16"), false},
- {"class c", args{subnet: parseCIDR("192.168.1.0/24")}, parseCIDR("192.168.2.0/24"), false},
- }
- for _, tt := range tests {
- test := tt
- t.Run(test.name, func(t *testing.T) {
- got, err := NextSubnet(test.args.subnet)
- if (err != nil) != test.wantErr {
- t.Errorf("NextSubnet() error = %v, wantErr %v", err, test.wantErr)
- return
- }
- if !reflect.DeepEqual(got, test.want) {
- t.Errorf("NextSubnet() got = %v, want %v", got, test.want)
- }
- })
- }
-}
-
-func TestFirstIPInSubnet(t *testing.T) {
- tests := []struct {
- name string
- args *net.IPNet
- want net.IP
- wantErr bool
- }{
- {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.0.1"), false},
- {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.1"), false},
- {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.0.1"), false},
- {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.1"), false},
- {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.129"), false},
- {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.0.0.1"), false},
- {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false},
- {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false},
- }
- for _, tt := range tests {
- test := tt
- t.Run(test.name, func(t *testing.T) {
- got, err := FirstIPInSubnet(test.args)
- if (err != nil) != test.wantErr {
- t.Errorf("FirstIPInSubnet() error = %v, wantErr %v", err, test.wantErr)
- return
- }
- if !got.Equal(test.want) {
- t.Errorf("FirstIPInSubnet() got = %v, want %v", got, test.want)
- }
- })
- }
-}
-
-func TestLastIPInSubnet(t *testing.T) {
- tests := []struct {
- name string
- args *net.IPNet
- want net.IP
- wantErr bool
- }{
- {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.255.255"), false},
- {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.255"), false},
- {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.1.255"), false},
- {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.127"), false},
- {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.191"), false},
- {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.255.255.255"), false},
- {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false},
- {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false},
- }
- for _, tt := range tests {
- test := tt
- t.Run(test.name, func(t *testing.T) {
- got, err := LastIPInSubnet(test.args)
- if (err != nil) != test.wantErr {
- t.Errorf("LastIPInSubnet() error = %v, wantErr %v", err, test.wantErr)
- return
- }
- if !got.Equal(test.want) {
- t.Errorf("LastIPInSubnet() got = %v, want %v", got, test.want)
- }
- })
- }
-}
diff --git a/libpod/network/types/network.go b/libpod/network/types/network.go
index c2c598f46..6053ceb29 100644
--- a/libpod/network/types/network.go
+++ b/libpod/network/types/network.go
@@ -23,6 +23,10 @@ type ContainerNetwork interface {
Setup(namespacePath string, options SetupOptions) (map[string]StatusBlock, error)
// Teardown will teardown the container network namespace.
Teardown(namespacePath string, options TeardownOptions) error
+
+ // Drivers will return the list of supported network drivers
+ // for this interface.
+ Drivers() []string
}
// Network describes the Network attributes.
@@ -36,8 +40,7 @@ type Network struct {
// InterfaceName is the network interface name on the host.
NetworkInterface string `json:"network_interface,omitempty"`
// Created contains the timestamp when this network was created.
- // This is not guaranteed to stay exactly the same.
- Created time.Time
+ Created time.Time `json:"created,omitempty"`
// Subnets to use.
Subnets []Subnet `json:"subnets,omitempty"`
// IPv6Enabled if set to true an ipv6 subnet should be created for this net.
@@ -92,9 +95,11 @@ func (n *IPNet) UnmarshalText(text []byte) error {
}
type Subnet struct {
- // Subnet for this Network.
+ // Subnet for this Network in CIDR form.
+ // swagger:strfmt string
Subnet IPNet `json:"subnet,omitempty"`
// Gateway IP for this Network.
+ // swagger:strfmt string
Gateway net.IP `json:"gateway,omitempty"`
// LeaseRange contains the range where IP are leased. Optional.
LeaseRange *LeaseRange `json:"lease_range,omitempty"`
@@ -103,8 +108,10 @@ type Subnet struct {
// LeaseRange contains the range where IP are leased.
type LeaseRange struct {
// StartIP first IP in the subnet which should be used to assign ips.
+ // swagger:strfmt string
StartIP net.IP `json:"start_ip,omitempty"`
// EndIP last IP in the subnet which should be used to assign ips.
+ // swagger:strfmt string
EndIP net.IP `json:"end_ip,omitempty"`
}
@@ -196,6 +203,20 @@ type PortMapping struct {
Protocol string `json:"protocol,omitempty"`
}
+// OCICNIPortMapping maps to the standard CNI portmapping Capability.
+// Deprecated, do not use this struct for new fields. This only exists
+// for backwards compatibility.
+type OCICNIPortMapping struct {
+ // HostPort is the port number on the host.
+ HostPort int32 `json:"hostPort"`
+ // ContainerPort is the port number inside the sandbox.
+ ContainerPort int32 `json:"containerPort"`
+ // Protocol is the protocol of the port mapping.
+ Protocol string `json:"protocol"`
+ // HostIP is the host ip to use.
+ HostIP string `json:"hostIP"`
+}
+
type SetupOptions struct {
NetworkOptions
}
diff --git a/libpod/network/util/filters.go b/libpod/network/util/filters.go
index 48e769196..c3c80b352 100644
--- a/libpod/network/util/filters.go
+++ b/libpod/network/util/filters.go
@@ -28,12 +28,6 @@ func createFilterFuncs(key string, filterValues []string) (types.FilterFunc, err
return util.StringMatchRegexSlice(net.Name, filterValues)
}, nil
- case "label":
- // matches all labels
- return func(net types.Network) bool {
- return util.MatchLabelFilters(filterValues, net.Labels)
- }, nil
-
case "driver":
// matches network driver
return func(net types.Network) bool {
@@ -46,9 +40,39 @@ func createFilterFuncs(key string, filterValues []string) (types.FilterFunc, err
return util.StringMatchRegexSlice(net.ID, filterValues)
}, nil
- // FIXME: What should we do with the old plugin filter
- // TODO: add dangling, dns enabled, internal filter
+ // TODO: add dns enabled, internal filter
+ }
+ return createPruneFilterFuncs(key, filterValues)
+}
+
+func GenerateNetworkPruneFilters(filters map[string][]string) ([]types.FilterFunc, error) {
+ filterFuncs := make([]types.FilterFunc, 0, len(filters))
+ for key, filterValues := range filters {
+ filterFunc, err := createPruneFilterFuncs(key, filterValues)
+ if err != nil {
+ return nil, err
+ }
+ filterFuncs = append(filterFuncs, filterFunc)
+ }
+ return filterFuncs, nil
+}
+
+func createPruneFilterFuncs(key string, filterValues []string) (types.FilterFunc, error) {
+ switch strings.ToLower(key) {
+ case "label":
+ // matches all labels
+ return func(net types.Network) bool {
+ return util.MatchLabelFilters(filterValues, net.Labels)
+ }, nil
+ case "until":
+ until, err := util.ComputeUntilTimestamp(filterValues)
+ if err != nil {
+ return nil, err
+ }
+ return func(net types.Network) bool {
+ return net.Created.Before(until)
+ }, nil
default:
return nil, errors.Errorf("invalid filter %q", key)
}
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index b0d4e0b2d..96b6fb298 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -11,17 +11,15 @@ import (
"os/exec"
"path/filepath"
"regexp"
- "sort"
"strconv"
"strings"
"syscall"
"time"
- cnitypes "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/events"
- "github.com/containers/podman/v3/libpod/network"
+ "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/pkg/errorhandling"
"github.com/containers/podman/v3/pkg/namespaces"
"github.com/containers/podman/v3/pkg/netns"
@@ -29,7 +27,6 @@ import (
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage/pkg/lockfile"
- "github.com/cri-o/ocicni/pkg/ocicni"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -51,58 +48,57 @@ const (
persistentCNIDir = "/var/lib/cni"
)
-// Get an OCICNI network config
-func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.PortMapping, staticIP net.IP, staticMAC net.HardwareAddr, netDescriptions ContainerNetworkDescriptions) ocicni.PodNetwork {
- var networkKey string
- if len(networks) > 0 {
- // This is inconsistent for >1 ctrNetwork, but it's probably the
- // best we can do.
- networkKey = networks[0]
- } else {
- networkKey = r.netPlugin.GetDefaultNetworkName()
- }
- ctrNetwork := ocicni.PodNetwork{
- Name: name,
- Namespace: name, // TODO is there something else we should put here? We don't know about Kube namespaces
- ID: id,
- NetNS: nsPath,
- RuntimeConfig: map[string]ocicni.RuntimeConfig{
- networkKey: {PortMappings: ports},
- },
+func (c *Container) getNetworkOptions() (types.NetworkOptions, error) {
+ opts := types.NetworkOptions{
+ ContainerID: c.config.ID,
+ ContainerName: getCNIPodName(c),
+ }
+ // TODO remove ocicni PortMappings from container config and store as types PortMappings
+ if len(c.config.PortMappings) > 0 {
+ opts.PortMappings = ocicniPortsToNetTypesPorts(c.config.PortMappings)
+ }
+ networks, _, err := c.networks()
+ if err != nil {
+ return opts, err
+ }
+ aliases, err := c.runtime.state.GetAllNetworkAliases(c)
+ if err != nil {
+ return opts, err
}
- // If we have extra networks, add them
- if len(networks) > 0 {
- ctrNetwork.Networks = make([]ocicni.NetAttachment, len(networks))
- for i, netName := range networks {
- ctrNetwork.Networks[i].Name = netName
- if eth, exists := netDescriptions.getInterfaceByName(netName); exists {
- ctrNetwork.Networks[i].Ifname = eth
- }
- }
+ // If the container requested special network options use this instead of the config.
+ // This is the case for container restore or network reload.
+ if c.perNetworkOpts != nil {
+ opts.Networks = c.perNetworkOpts
+ return opts, nil
}
- if staticIP != nil || staticMAC != nil {
- // For static IP or MAC, we need to populate networks even if
- // it's just the default.
- if len(networks) == 0 {
- // If len(networks) == 0 this is guaranteed to be the
- // default ctrNetwork.
- ctrNetwork.Networks = []ocicni.NetAttachment{{Name: networkKey}}
- }
- var rt ocicni.RuntimeConfig = ocicni.RuntimeConfig{PortMappings: ports}
- if staticIP != nil {
- rt.IP = staticIP.String()
+ // Update container map of interface descriptions
+ if err := c.setupNetworkDescriptions(networks); err != nil {
+ return opts, err
+ }
+
+ nets := make(map[string]types.PerNetworkOptions, len(networks))
+ for i, network := range networks {
+ eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(network)
+ if !exists {
+ return opts, errors.Errorf("no network interface name for container %s on network %s", c.config.ID, network)
}
- if staticMAC != nil {
- rt.MAC = staticMAC.String()
+ netOpts := types.PerNetworkOptions{
+ InterfaceName: eth,
+ Aliases: aliases[network],
}
- ctrNetwork.RuntimeConfig = map[string]ocicni.RuntimeConfig{
- networkKey: rt,
+ // only set the static ip/mac on the first network
+ if i == 0 {
+ if c.config.StaticIP != nil {
+ netOpts.StaticIPs = []net.IP{c.config.StaticIP}
+ }
+ netOpts.StaticMAC = c.config.StaticMAC
}
+ nets[network] = netOpts
}
-
- return ctrNetwork
+ opts.Networks = nets
+ return opts, nil
}
type RootlessCNI struct {
@@ -571,9 +567,9 @@ func setPrimaryMachineIP() error {
return os.Setenv("PODMAN_MACHINE_HOST", addr.IP.String())
}
-// setUpOCICNIPod will set up the cni networks, on error it will also tear down the cni
+// setUpNetwork will set up the the networks, on error it will also tear down the cni
// networks. If rootless it will join/create the rootless cni namespace.
-func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResult, error) {
+func (r *Runtime) setUpNetwork(ns string, opts types.NetworkOptions) (map[string]types.StatusBlock, error) {
if r.config.MachineEnabled() {
if err := setPrimaryMachineIP(); err != nil {
return nil, err
@@ -583,16 +579,10 @@ func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResu
if err != nil {
return nil, err
}
- var results []ocicni.NetResult
+ var results map[string]types.StatusBlock
setUpPod := func() error {
- results, err = r.netPlugin.SetUpPod(podNetwork)
- if err != nil {
- if err2 := r.netPlugin.TearDownPod(podNetwork); err2 != nil {
- logrus.Errorf("Error tearing down partially created network namespace for container %s: %v", podNetwork.ID, err2)
- }
- return errors.Wrapf(err, "error configuring network namespace for container %s", podNetwork.ID)
- }
- return nil
+ results, err = r.network.Setup(ns, types.SetupOptions{NetworkOptions: opts})
+ return err
}
// rootlessCNINS is nil if we are root
if rootlessCNINS != nil {
@@ -609,7 +599,7 @@ func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResu
// If we are in the pod network namespace use the pod name otherwise the container name
func getCNIPodName(c *Container) string {
if c.config.NetMode.IsPod() || c.IsInfra() {
- pod, err := c.runtime.GetPod(c.PodID())
+ pod, err := c.runtime.state.Pod(c.PodID())
if err == nil {
return pod.Name()
}
@@ -618,26 +608,7 @@ func getCNIPodName(c *Container) string {
}
// Create and configure a new network namespace for a container
-func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Result, error) {
- var requestedIP net.IP
- if ctr.requestedIP != nil {
- requestedIP = ctr.requestedIP
- // cancel request for a specific IP in case the container is reused later
- ctr.requestedIP = nil
- } else {
- requestedIP = ctr.config.StaticIP
- }
-
- var requestedMAC net.HardwareAddr
- if ctr.requestedMAC != nil {
- requestedMAC = ctr.requestedMAC
- // cancel request for a specific MAC in case the container is reused later
- ctr.requestedMAC = nil
- } else {
- requestedMAC = ctr.config.StaticMAC
- }
-
- podName := getCNIPodName(ctr)
+func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (map[string]types.StatusBlock, error) {
networks, _, err := ctr.networks()
if err != nil {
return nil, err
@@ -648,39 +619,15 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re
return nil, nil
}
- // Update container map of interface descriptions
- if err := ctr.setupNetworkDescriptions(networks); err != nil {
- return nil, err
- }
- podNetwork := r.getPodNetwork(ctr.ID(), podName, ctrNS.Path(), networks, ctr.config.PortMappings, requestedIP, requestedMAC, ctr.state.NetInterfaceDescriptions)
- aliases, err := ctr.runtime.state.GetAllNetworkAliases(ctr)
+ netOpts, err := ctr.getNetworkOptions()
if err != nil {
return nil, err
}
- if len(aliases) > 0 {
- podNetwork.Aliases = aliases
- }
-
- results, err := r.setUpOCICNIPod(podNetwork)
- if err != nil {
- return nil, err
- }
-
- networkStatus := make([]*cnitypes.Result, 0)
- for idx, r := range results {
- logrus.Debugf("[%d] CNI result: %v", idx, r.Result)
- resultCurrent, err := cnitypes.GetResult(r.Result)
- if err != nil {
- return nil, errors.Wrapf(err, "error parsing CNI plugin result %q: %v", r.Result, err)
- }
- networkStatus = append(networkStatus, resultCurrent)
- }
-
- return networkStatus, nil
+ return r.setUpNetwork(ctrNS.Path(), netOpts)
}
// Create and configure a new network namespace for a container
-func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result, retErr error) {
+func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q map[string]types.StatusBlock, retErr error) {
ctrNS, err := netns.NewNS()
if err != nil {
return nil, nil, errors.Wrapf(err, "error creating network namespace for container %s", ctr.ID())
@@ -698,7 +645,7 @@ func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result,
logrus.Debugf("Made network namespace at %s for container %s", ctrNS.Path(), ctr.ID())
- networkStatus := []*cnitypes.Result{}
+ var networkStatus map[string]types.StatusBlock
if !ctr.config.NetMode.IsSlirp4netns() {
networkStatus, err = r.configureNetNS(ctr, ctrNS)
}
@@ -797,14 +744,14 @@ func (r *Runtime) closeNetNS(ctr *Container) error {
// Tear down a container's CNI network configuration and joins the
// rootless net ns as rootless user
-func (r *Runtime) teardownOCICNIPod(podNetwork ocicni.PodNetwork) error {
+func (r *Runtime) teardownNetwork(ns string, opts types.NetworkOptions) error {
rootlessCNINS, err := r.GetRootlessCNINetNs(false)
if err != nil {
return err
}
tearDownPod := func() error {
- err := r.netPlugin.TearDownPod(podNetwork)
- return errors.Wrapf(err, "error tearing down CNI namespace configuration for container %s", podNetwork.ID)
+ err := r.network.Teardown(ns, types.TeardownOptions{NetworkOptions: opts})
+ return errors.Wrapf(err, "error tearing down network namespace configuration for container %s", opts.ContainerID)
}
// rootlessCNINS is nil if we are root
@@ -837,27 +784,11 @@ func (r *Runtime) teardownCNI(ctr *Container) error {
}
if !ctr.config.NetMode.IsSlirp4netns() && len(networks) > 0 {
- var requestedIP net.IP
- if ctr.requestedIP != nil {
- requestedIP = ctr.requestedIP
- // cancel request for a specific IP in case the container is reused later
- ctr.requestedIP = nil
- } else {
- requestedIP = ctr.config.StaticIP
- }
-
- var requestedMAC net.HardwareAddr
- if ctr.requestedMAC != nil {
- requestedMAC = ctr.requestedMAC
- // cancel request for a specific MAC in case the container is reused later
- ctr.requestedMAC = nil
- } else {
- requestedMAC = ctr.config.StaticMAC
+ netOpts, err := ctr.getNetworkOptions()
+ if err != nil {
+ return err
}
-
- podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), networks, ctr.config.PortMappings, requestedIP, requestedMAC, ctr.state.NetInterfaceDescriptions)
- err = r.teardownOCICNIPod(podNetwork)
- return err
+ return r.teardownNetwork(ctr.state.NetNS.Path(), netOpts)
}
return nil
}
@@ -918,40 +849,15 @@ func isBridgeNetMode(n namespaces.NetworkMode) error {
// single MAC or IP.
// Only works on root containers at present, though in the future we could
// extend this to stop + restart slirp4netns
-func (r *Runtime) reloadContainerNetwork(ctr *Container) ([]*cnitypes.Result, error) {
+func (r *Runtime) reloadContainerNetwork(ctr *Container) (map[string]types.StatusBlock, error) {
if ctr.state.NetNS == nil {
return nil, errors.Wrapf(define.ErrCtrStateInvalid, "container %s network is not configured, refusing to reload", ctr.ID())
}
if err := isBridgeNetMode(ctr.config.NetMode); err != nil {
return nil, err
}
-
logrus.Infof("Going to reload container %s network", ctr.ID())
- var requestedIP net.IP
- var requestedMAC net.HardwareAddr
- // Set requested IP and MAC address, if possible.
- if len(ctr.state.NetworkStatus) == 1 {
- result := ctr.state.NetworkStatus[0]
- if len(result.IPs) == 1 {
- resIP := result.IPs[0]
-
- requestedIP = resIP.Address.IP
- ctr.requestedIP = requestedIP
- logrus.Debugf("Going to preserve container %s IP address %s", ctr.ID(), ctr.requestedIP.String())
-
- if resIP.Interface != nil && *resIP.Interface < len(result.Interfaces) && *resIP.Interface >= 0 {
- var err error
- requestedMAC, err = net.ParseMAC(result.Interfaces[*resIP.Interface].Mac)
- if err != nil {
- return nil, errors.Wrapf(err, "error parsing container %s MAC address %s", ctr.ID(), result.Interfaces[*resIP.Interface].Mac)
- }
- ctr.requestedMAC = requestedMAC
- logrus.Debugf("Going to preserve container %s MAC address %s", ctr.ID(), ctr.requestedMAC.String())
- }
- }
- }
-
err := r.teardownCNI(ctr)
if err != nil {
// teardownCNI will error if the iptables rules do not exists and this is the case after
@@ -966,9 +872,40 @@ func (r *Runtime) reloadContainerNetwork(ctr *Container) ([]*cnitypes.Result, er
}
}
- // teardownCNI will clean the requested IP and MAC so we need to set them again
- ctr.requestedIP = requestedIP
- ctr.requestedMAC = requestedMAC
+ aliases, err := ctr.runtime.state.GetAllNetworkAliases(ctr)
+ if err != nil {
+ return nil, err
+ }
+
+ // Set the same network settings as before..
+ netStatus := ctr.getNetworkStatus()
+ netOpts := make(map[string]types.PerNetworkOptions, len(netStatus))
+ for network, status := range netStatus {
+ perNetOpts := types.PerNetworkOptions{}
+ for name, netInt := range status.Interfaces {
+ perNetOpts = types.PerNetworkOptions{
+ InterfaceName: name,
+ Aliases: aliases[network],
+ StaticMAC: netInt.MacAddress,
+ }
+ for _, netAddress := range netInt.Networks {
+ perNetOpts.StaticIPs = append(perNetOpts.StaticIPs, netAddress.Subnet.IP)
+ }
+ // Normally interfaces have a length of 1, only for some special cni configs we could get more.
+ // For now just use the first interface to get the ips this should be good enough for most cases.
+ break
+ }
+ if perNetOpts.InterfaceName == "" {
+ eth, exists := ctr.state.NetInterfaceDescriptions.getInterfaceByName(network)
+ if !exists {
+ return nil, errors.Errorf("no network interface name for container %s on network %s", ctr.config.ID, network)
+ }
+ perNetOpts.InterfaceName = eth
+ }
+ netOpts[network] = perNetOpts
+ }
+ ctr.perNetworkOpts = netOpts
+
return r.configureNetNS(ctr, ctr.state.NetNS)
}
@@ -989,7 +926,8 @@ func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) {
return nil, nil
}
err := ns.WithNetNSPath(netNSPath, func(_ ns.NetNS) error {
- link, err := netlink.LinkByName(ocicni.DefaultInterfaceName)
+ // FIXME get the interface from the container netstatus
+ link, err := netlink.LinkByName("eth0")
if err != nil {
return err
}
@@ -1047,27 +985,26 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
// Set network namespace path
settings.SandboxKey = c.state.NetNS.Path()
+ netStatus := c.getNetworkStatus()
// If this is empty, we're probably slirp4netns
- if len(c.state.NetworkStatus) == 0 {
+ if len(netStatus) == 0 {
return settings, nil
}
- // If we have CNI networks - handle that here
+ // If we have networks - handle that here
if len(networks) > 0 {
- if len(networks) != len(c.state.NetworkStatus) {
- return nil, errors.Wrapf(define.ErrInternal, "network inspection mismatch: asked to join %d CNI network(s) %v, but have information on %d network(s)", len(networks), networks, len(c.state.NetworkStatus))
+ if len(networks) != len(netStatus) {
+ return nil, errors.Wrapf(define.ErrInternal, "network inspection mismatch: asked to join %d network(s) %v, but have information on %d network(s)", len(networks), networks, len(netStatus))
}
settings.Networks = make(map[string]*define.InspectAdditionalNetwork)
- // CNI results should be in the same order as the list of
- // networks we pass into CNI.
- for index, name := range networks {
- cniResult := c.state.NetworkStatus[index]
+ for _, name := range networks {
+ result := netStatus[name]
addedNet := new(define.InspectAdditionalNetwork)
addedNet.NetworkID = name
- basicConfig, err := resultToBasicNetworkConfig(cniResult)
+ basicConfig, err := resultToBasicNetworkConfig(result)
if err != nil {
return nil, err
}
@@ -1089,19 +1026,19 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
}
// If not joining networks, we should have at most 1 result
- if len(c.state.NetworkStatus) > 1 {
- return nil, errors.Wrapf(define.ErrInternal, "should have at most 1 CNI result if not joining networks, instead got %d", len(c.state.NetworkStatus))
+ if len(netStatus) > 1 {
+ return nil, errors.Wrapf(define.ErrInternal, "should have at most 1 network status result if not joining networks, instead got %d", len(netStatus))
}
- if len(c.state.NetworkStatus) == 1 {
- basicConfig, err := resultToBasicNetworkConfig(c.state.NetworkStatus[0])
- if err != nil {
- return nil, err
+ if len(netStatus) == 1 {
+ for _, status := range netStatus {
+ basicConfig, err := resultToBasicNetworkConfig(status)
+ if err != nil {
+ return nil, err
+ }
+ settings.InspectBasicNetworkConfig = basicConfig
}
-
- settings.InspectBasicNetworkConfig = basicConfig
}
-
return settings, nil
}
@@ -1130,45 +1067,40 @@ func (c *Container) setupNetworkDescriptions(networks []string) error {
// resultToBasicNetworkConfig produces an InspectBasicNetworkConfig from a CNI
// result
-func resultToBasicNetworkConfig(result *cnitypes.Result) (define.InspectBasicNetworkConfig, error) {
+func resultToBasicNetworkConfig(result types.StatusBlock) (define.InspectBasicNetworkConfig, error) {
config := define.InspectBasicNetworkConfig{}
-
- for _, ctrIP := range result.IPs {
- size, _ := ctrIP.Address.Mask.Size()
- switch {
- case ctrIP.Version == "4" && config.IPAddress == "":
- config.IPAddress = ctrIP.Address.IP.String()
- config.IPPrefixLen = size
- config.Gateway = ctrIP.Gateway.String()
- if ctrIP.Interface != nil && *ctrIP.Interface < len(result.Interfaces) && *ctrIP.Interface >= 0 {
- config.MacAddress = result.Interfaces[*ctrIP.Interface].Mac
- }
- case ctrIP.Version == "4" && config.IPAddress != "":
- config.SecondaryIPAddresses = append(config.SecondaryIPAddresses, ctrIP.Address.String())
- if ctrIP.Interface != nil && *ctrIP.Interface < len(result.Interfaces) && *ctrIP.Interface >= 0 {
- config.AdditionalMacAddresses = append(config.AdditionalMacAddresses, result.Interfaces[*ctrIP.Interface].Mac)
+ for _, netInt := range result.Interfaces {
+ for _, netAddress := range netInt.Networks {
+ size, _ := netAddress.Subnet.Mask.Size()
+ if netAddress.Subnet.IP.To4() != nil {
+ //ipv4
+ if config.IPAddress == "" {
+ config.IPAddress = netAddress.Subnet.IP.String()
+ config.IPPrefixLen = size
+ config.Gateway = netAddress.Gateway.String()
+ } else {
+ config.SecondaryIPAddresses = append(config.SecondaryIPAddresses, netAddress.Subnet.IP.String())
+ }
+ } else {
+ //ipv6
+ if config.GlobalIPv6Address == "" {
+ config.GlobalIPv6Address = netAddress.Subnet.IP.String()
+ config.GlobalIPv6PrefixLen = size
+ config.IPv6Gateway = netAddress.Gateway.String()
+ } else {
+ config.SecondaryIPv6Addresses = append(config.SecondaryIPv6Addresses, netAddress.Subnet.IP.String())
+ }
}
- case ctrIP.Version == "6" && config.IPAddress == "":
- config.GlobalIPv6Address = ctrIP.Address.IP.String()
- config.GlobalIPv6PrefixLen = size
- config.IPv6Gateway = ctrIP.Gateway.String()
- case ctrIP.Version == "6" && config.IPAddress != "":
- config.SecondaryIPv6Addresses = append(config.SecondaryIPv6Addresses, ctrIP.Address.String())
- default:
- return config, errors.Wrapf(define.ErrInternal, "unrecognized IP version %q", ctrIP.Version)
+ }
+ if config.MacAddress == "" {
+ config.MacAddress = netInt.MacAddress.String()
+ } else {
+ config.AdditionalMacAddresses = append(config.AdditionalMacAddresses, netInt.MacAddress.String())
}
}
-
return config, nil
}
-// This is a horrible hack, necessary because CNI does not properly clean up
-// after itself on an unclean reboot. Return what we're pretty sure is the path
-// to CNI's internal files (it's not really exposed to us).
-func getCNINetworksDir() (string, error) {
- return filepath.Join(persistentCNIDir, "networks"), nil
-}
-
type logrusDebugWriter struct {
prefix string
}
@@ -1185,28 +1117,31 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro
return err
}
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
networks, err := c.networksByNameIndex()
if err != nil {
return err
}
// check if network exists and if the input is a ID we get the name
- // ocicni only uses names so it is important that we only use the name
- netName, err = network.NormalizeName(c.runtime.config, netName)
+ // CNI only uses names so it is important that we only use the name
+ netName, err = c.runtime.normalizeNetworkName(netName)
if err != nil {
return err
}
- index, nameExists := networks[netName]
+ _, nameExists := networks[netName]
if !nameExists && len(networks) > 0 {
return errors.Errorf("container %s is not connected to network %s", nameOrID, netName)
}
- c.lock.Lock()
- defer c.lock.Unlock()
if err := c.syncContainer(); err != nil {
return err
}
+ // get network status before we disconnect
+ networkStatus := c.getNetworkStatus()
if err := c.runtime.state.NetworkDisconnect(c, netName); err != nil {
return err
@@ -1221,41 +1156,38 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro
return errors.Wrapf(define.ErrNoNetwork, "unable to disconnect %s from %s", nameOrID, netName)
}
- podConfig := c.runtime.getPodNetwork(c.ID(), c.Name(), c.state.NetNS.Path(), []string{netName}, c.config.PortMappings, nil, nil, c.state.NetInterfaceDescriptions)
- if err := c.runtime.teardownOCICNIPod(podConfig); err != nil {
- return err
+ opts := types.NetworkOptions{
+ ContainerID: c.config.ID,
+ ContainerName: getCNIPodName(c),
+ }
+ if len(c.config.PortMappings) > 0 {
+ opts.PortMappings = ocicniPortsToNetTypesPorts(c.config.PortMappings)
+ }
+ eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(netName)
+ if !exists {
+ return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, netName)
+ }
+ opts.Networks = map[string]types.PerNetworkOptions{
+ netName: {
+ InterfaceName: eth,
+ },
}
- // update network status if container is not running
- networkStatus := c.state.NetworkStatus
- // clip out the index of the network
- tmpNetworkStatus := make([]*cnitypes.Result, 0, len(networkStatus)-1)
- for k, v := range networkStatus {
- if index != k {
- tmpNetworkStatus = append(tmpNetworkStatus, v)
- }
+ if err := c.runtime.teardownNetwork(c.state.NetNS.Path(), opts); err != nil {
+ return err
}
- c.state.NetworkStatus = tmpNetworkStatus
+
+ // update network status if container is running
+ delete(networkStatus, netName)
+ c.state.NetworkStatus = networkStatus
err = c.save()
if err != nil {
return err
}
- // OCICNI will set the loopback adapter down on teardown so we should set it up again
- err = c.state.NetNS.Do(func(_ ns.NetNS) error {
- link, err := netlink.LinkByName("lo")
- if err != nil {
- return err
- }
- err = netlink.LinkSetUp(link)
- return err
- })
- if err != nil {
- logrus.Warnf("failed to set loopback adapter up in the container: %v", err)
- }
// Reload ports when there are still connected networks, maybe we removed the network interface with the child ip.
// Reloading without connected networks does not make sense, so we can skip this step.
- if rootless.IsRootless() && len(tmpNetworkStatus) > 0 {
+ if rootless.IsRootless() && len(networkStatus) > 0 {
return c.reloadRootlessRLKPortMapping()
}
return nil
@@ -1268,24 +1200,28 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e
return err
}
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
networks, err := c.networksByNameIndex()
if err != nil {
return err
}
// check if network exists and if the input is a ID we get the name
- // ocicni only uses names so it is important that we only use the name
- netName, err = network.NormalizeName(c.runtime.config, netName)
+ // CNI only uses names so it is important that we only use the name
+ netName, err = c.runtime.normalizeNetworkName(netName)
if err != nil {
return err
}
- c.lock.Lock()
- defer c.lock.Unlock()
if err := c.syncContainer(); err != nil {
return err
}
+ // get network status before we connect
+ networkStatus := c.getNetworkStatus()
+
if err := c.runtime.state.NetworkConnect(c, netName, aliases); err != nil {
return err
}
@@ -1305,10 +1241,26 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e
if err := c.setupNetworkDescriptions(ctrNetworks); err != nil {
return err
}
- podConfig := c.runtime.getPodNetwork(c.ID(), c.Name(), c.state.NetNS.Path(), []string{netName}, c.config.PortMappings, nil, nil, c.state.NetInterfaceDescriptions)
- podConfig.Aliases = make(map[string][]string, 1)
- podConfig.Aliases[netName] = aliases
- results, err := c.runtime.setUpOCICNIPod(podConfig)
+
+ opts := types.NetworkOptions{
+ ContainerID: c.config.ID,
+ ContainerName: getCNIPodName(c),
+ }
+ if len(c.config.PortMappings) > 0 {
+ opts.PortMappings = ocicniPortsToNetTypesPorts(c.config.PortMappings)
+ }
+ eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(netName)
+ if !exists {
+ return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, netName)
+ }
+ opts.Networks = map[string]types.PerNetworkOptions{
+ netName: {
+ Aliases: aliases,
+ InterfaceName: eth,
+ },
+ }
+
+ results, err := c.runtime.setUpNetwork(c.state.NetNS.Path(), opts)
if err != nil {
return err
}
@@ -1316,40 +1268,13 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e
return errors.New("when adding aliases, results must be of length 1")
}
- networkResults := make([]*cnitypes.Result, 0)
- for _, r := range results {
- resultCurrent, err := cnitypes.GetResult(r.Result)
- if err != nil {
- return errors.Wrapf(err, "error parsing CNI plugin result %q: %v", r.Result, err)
- }
- networkResults = append(networkResults, resultCurrent)
- }
-
// update network status
- networkStatus := c.state.NetworkStatus
- // if len is one and we confirmed earlier that the container is in
- // fact connected to the network, then just return an empty slice
- if len(networkStatus) == 0 {
- c.state.NetworkStatus = append(c.state.NetworkStatus, networkResults...)
- } else {
- // build a list of network names so we can sort and
- // get the new name's index
- var networkNames []string
- for name := range networks {
- networkNames = append(networkNames, name)
- }
- networkNames = append(networkNames, netName)
- // sort
- sort.Strings(networkNames)
- // get index of new network name
- index := sort.SearchStrings(networkNames, netName)
- // Append a zero value to to the slice
- networkStatus = append(networkStatus, &cnitypes.Result{})
- // populate network status
- copy(networkStatus[index+1:], networkStatus[index:])
- networkStatus[index] = networkResults[0]
- c.state.NetworkStatus = networkStatus
+ if networkStatus == nil {
+ networkStatus = make(map[string]types.StatusBlock, 1)
}
+ networkStatus[netName] = results[netName]
+ c.state.NetworkStatus = networkStatus
+
err = c.save()
if err != nil {
return err
@@ -1379,3 +1304,27 @@ func (r *Runtime) ConnectContainerToNetwork(nameOrID, netName string, aliases []
}
return ctr.NetworkConnect(nameOrID, netName, aliases)
}
+
+// normalizeNetworkName takes a network name, a partial or a full network ID and returns the network name.
+// If the network is not found a errors is returned.
+func (r *Runtime) normalizeNetworkName(nameOrID string) (string, error) {
+ net, err := r.network.NetworkInspect(nameOrID)
+ if err != nil {
+ return "", err
+ }
+ return net.Name, nil
+}
+
+func ocicniPortsToNetTypesPorts(ports []types.OCICNIPortMapping) []types.PortMapping {
+ newPorts := make([]types.PortMapping, 0, len(ports))
+ for _, port := range ports {
+ newPorts = append(newPorts, types.PortMapping{
+ HostIP: port.HostIP,
+ HostPort: uint16(port.HostPort),
+ ContainerPort: uint16(port.ContainerPort),
+ Protocol: port.Protocol,
+ Range: 1,
+ })
+ }
+ return newPorts
+}
diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go
index a09027b72..a5c035757 100644
--- a/libpod/networking_slirp4netns.go
+++ b/libpod/networking_slirp4netns.go
@@ -615,14 +615,21 @@ func getRootlessPortChildIP(c *Container) string {
return slirp4netnsIP.String()
}
- for _, r := range c.state.NetworkStatus {
- for _, i := range r.IPs {
- ipv4 := i.Address.IP.To4()
- if ipv4 != nil {
- return ipv4.String()
+ var ipv6 net.IP
+ for _, status := range c.getNetworkStatus() {
+ for _, netInt := range status.Interfaces {
+ for _, netAddress := range netInt.Networks {
+ ipv4 := netAddress.Subnet.IP.To4()
+ if ipv4 != nil {
+ return ipv4.String()
+ }
+ ipv6 = netAddress.Subnet.IP
}
}
}
+ if ipv6 != nil {
+ return ipv6.String()
+ }
return ""
}
diff --git a/libpod/oci_util.go b/libpod/oci_util.go
index f2843b09b..7db267915 100644
--- a/libpod/oci_util.go
+++ b/libpod/oci_util.go
@@ -9,7 +9,7 @@ import (
"time"
"github.com/containers/podman/v3/libpod/define"
- "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/containers/podman/v3/libpod/network/types"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -32,7 +32,7 @@ func createUnitName(prefix string, name string) string {
}
// Bind ports to keep them closed on the host
-func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) {
+func bindPorts(ports []types.OCICNIPortMapping) ([]*os.File, error) {
var files []*os.File
notifySCTP := false
for _, i := range ports {
diff --git a/libpod/options.go b/libpod/options.go
index 7b0c6641a..3f6ccf1cb 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -14,14 +14,13 @@ import (
"github.com/containers/image/v5/types"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/events"
- netTypes "github.com/containers/podman/v3/libpod/network/types"
+ nettypes "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/pkg/namespaces"
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
- "github.com/cri-o/ocicni/pkg/ocicni"
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -1040,7 +1039,7 @@ func WithDependencyCtrs(ctrs []*Container) CtrCreateOption {
// namespace with a minimal configuration.
// An optional array of port mappings can be provided.
// Conflicts with WithNetNSFrom().
-func WithNetNS(portMappings []ocicni.PortMapping, exposedPorts map[uint16][]string, postConfigureNetNS bool, netmode string, networks []string) CtrCreateOption {
+func WithNetNS(portMappings []nettypes.OCICNIPortMapping, exposedPorts map[uint16][]string, postConfigureNetNS bool, netmode string, networks []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
@@ -2063,10 +2062,10 @@ func WithInfraContainer() PodCreateOption {
}
// WithInfraContainerPorts tells the pod to add port bindings to the pause container
-func WithInfraContainerPorts(bindings []ocicni.PortMapping, infraSpec *specgen.SpecGenerator) []netTypes.PortMapping {
- bindingSpec := []netTypes.PortMapping{}
+func WithInfraContainerPorts(bindings []nettypes.OCICNIPortMapping, infraSpec *specgen.SpecGenerator) []nettypes.PortMapping {
+ bindingSpec := []nettypes.PortMapping{}
for _, bind := range bindings {
- currBind := netTypes.PortMapping{}
+ currBind := nettypes.PortMapping{}
currBind.ContainerPort = uint16(bind.ContainerPort)
currBind.HostIP = bind.HostIP
currBind.HostPort = uint16(bind.HostPort)
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 761fa08a2..d2b3d36da 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -20,7 +20,6 @@ import (
"github.com/containers/buildah/pkg/parse"
"github.com/containers/common/libimage"
"github.com/containers/common/pkg/config"
- "github.com/containers/common/pkg/defaultnet"
"github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/pkg/sysregistriesv2"
is "github.com/containers/image/v5/storage"
@@ -28,6 +27,8 @@ import (
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/events"
"github.com/containers/podman/v3/libpod/lock"
+ "github.com/containers/podman/v3/libpod/network/cni"
+ nettypes "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/libpod/plugin"
"github.com/containers/podman/v3/libpod/shutdown"
"github.com/containers/podman/v3/pkg/cgroups"
@@ -36,7 +37,6 @@ import (
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage"
"github.com/containers/storage/pkg/unshare"
- "github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/docker/pkg/namesgenerator"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
@@ -80,7 +80,7 @@ type Runtime struct {
defaultOCIRuntime OCIRuntime
ociRuntimes map[string]OCIRuntime
runtimeFlags []string
- netPlugin ocicni.CNIPlugin
+ network nettypes.ContainerNetwork
conmonPath string
libimageRuntime *libimage.Runtime
libimageEventsShutdown chan bool
@@ -482,17 +482,20 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
}
}
- // If we need to make a default network - do so now.
- if err := defaultnet.Create(runtime.config.Network.DefaultNetwork, runtime.config.Network.DefaultSubnet, runtime.config.Network.NetworkConfigDir, runtime.config.Engine.StaticDir, runtime.config.Engine.MachineEnabled); err != nil {
- logrus.Errorf("Failed to created default CNI network: %v", err)
- }
-
- // Set up the CNI net plugin
- netPlugin, err := ocicni.InitCNINoInotify(runtime.config.Network.DefaultNetwork, runtime.config.Network.NetworkConfigDir, "", runtime.config.Network.CNIPluginDirs...)
+ netInterface, err := cni.NewCNINetworkInterface(cni.InitConfig{
+ CNIConfigDir: runtime.config.Network.NetworkConfigDir,
+ CNIPluginDirs: runtime.config.Network.CNIPluginDirs,
+ DefaultNetwork: runtime.config.Network.DefaultNetwork,
+ DefaultSubnet: runtime.config.Network.DefaultSubnet,
+ IsMachine: runtime.config.Engine.MachineEnabled,
+ // TODO use cni.lock
+ LockFile: filepath.Join(runtime.config.Network.NetworkConfigDir, "cni1.lock"),
+ })
if err != nil {
- return errors.Wrapf(err, "error configuring CNI network plugin")
+ return errors.Wrapf(err, "could not create network interface")
}
- runtime.netPlugin = netPlugin
+
+ runtime.network = netInterface
// We now need to see if the system has restarted
// We check for the presence of a file in our tmp directory to verify this
@@ -1166,3 +1169,13 @@ func (r *Runtime) graphRootMountedFlag(mounts []spec.Mount) string {
}
return ""
}
+
+// Network returns the network interface which is used by the runtime
+func (r *Runtime) Network() nettypes.ContainerNetwork {
+ return r.network
+}
+
+// Network returns the network interface which is used by the runtime
+func (r *Runtime) GetDefaultNetworkName() string {
+ return r.config.Network.DefaultNetwork
+}
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 7d3891f6e..d4f67a115 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -12,7 +12,6 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/events"
- "github.com/containers/podman/v3/libpod/network"
"github.com/containers/podman/v3/libpod/shutdown"
"github.com/containers/podman/v3/pkg/cgroups"
"github.com/containers/podman/v3/pkg/domain/entities/reports"
@@ -248,7 +247,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
if len(ctr.config.Networks) > 0 {
netNames := make([]string, 0, len(ctr.config.Networks))
for _, nameOrID := range ctr.config.Networks {
- netName, err := network.NormalizeName(r.config, nameOrID)
+ netName, err := r.normalizeNetworkName(nameOrID)
if err != nil {
return nil, err
}
@@ -262,7 +261,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
if len(ctr.config.NetworkAliases) > 0 {
netAliases := make(map[string][]string, len(ctr.config.NetworkAliases))
for nameOrID, aliases := range ctr.config.NetworkAliases {
- netName, err := network.NormalizeName(r.config, nameOrID)
+ netName, err := r.normalizeNetworkName(nameOrID)
if err != nil {
return nil, err
}
diff --git a/libpod/util.go b/libpod/util.go
index ed5c4e6c6..d3f7da91e 100644
--- a/libpod/util.go
+++ b/libpod/util.go
@@ -15,8 +15,8 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod/define"
+ "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/utils"
- "github.com/cri-o/ocicni/pkg/ocicni"
"github.com/fsnotify/fsnotify"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/selinux/go-selinux/label"
@@ -295,8 +295,8 @@ func writeHijackHeader(r *http.Request, conn io.Writer) {
}
// Convert OCICNI port bindings into Inspect-formatted port bindings.
-func makeInspectPortBindings(bindings []ocicni.PortMapping, expose map[uint16][]string) map[string][]define.InspectHostPort {
- portBindings := make(map[string][]define.InspectHostPort, len(bindings))
+func makeInspectPortBindings(bindings []types.OCICNIPortMapping, expose map[uint16][]string) map[string][]define.InspectHostPort {
+ portBindings := make(map[string][]define.InspectHostPort)
for _, port := range bindings {
key := fmt.Sprintf("%d/%s", port.ContainerPort, port.Protocol)
hostPorts := portBindings[key]