summaryrefslogtreecommitdiff
path: root/pkg/specgen/namespaces.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/specgen/namespaces.go')
-rw-r--r--pkg/specgen/namespaces.go177
1 files changed, 163 insertions, 14 deletions
diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go
index bb5385ef1..15a8ece17 100644
--- a/pkg/specgen/namespaces.go
+++ b/pkg/specgen/namespaces.go
@@ -2,10 +2,13 @@ package specgen
import (
"fmt"
+ "net"
"os"
"strings"
"github.com/containers/common/pkg/cgroups"
+ "github.com/containers/podman/v3/libpod/define"
+ "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage"
@@ -271,9 +274,9 @@ func ParseUserNamespace(ns string) (Namespace, error) {
// ParseNetworkNamespace parses a network namespace specification in string
// form.
// Returns a namespace and (optionally) a list of CNI networks to join.
-func ParseNetworkNamespace(ns string, rootlessDefaultCNI bool) (Namespace, []string, error) {
+func ParseNetworkNamespace(ns string, rootlessDefaultCNI bool) (Namespace, map[string]types.PerNetworkOptions, error) {
toReturn := Namespace{}
- var cniNetworks []string
+ networks := make(map[string]types.PerNetworkOptions)
// Net defaults to Slirp on rootless
switch {
case ns == string(Slirp), strings.HasPrefix(ns, string(Slirp)+":"):
@@ -313,28 +316,174 @@ func ParseNetworkNamespace(ns string, rootlessDefaultCNI bool) (Namespace, []str
default:
// Assume we have been given a list of CNI networks.
// Which only works in bridge mode, so set that.
- cniNetworks = strings.Split(ns, ",")
+ networkList := strings.Split(ns, ",")
+ for _, net := range networkList {
+ networks[net] = types.PerNetworkOptions{}
+ }
+
toReturn.NSMode = Bridge
}
- return toReturn, cniNetworks, nil
+ return toReturn, networks, nil
}
-func ParseNetworkString(network string) (Namespace, []string, map[string][]string, error) {
+// ParseNetworkFlag parses a network string slice into the network options
+// If the input is nil or empty it will use the default setting from containers.conf
+func ParseNetworkFlag(networks []string) (Namespace, map[string]types.PerNetworkOptions, map[string][]string, error) {
var networkOptions map[string][]string
- parts := strings.SplitN(network, ":", 2)
+ // by default we try to use the containers.conf setting
+ // if we get at least one value use this instead
+ ns := containerConfig.Containers.NetNS
+ if len(networks) > 0 {
+ ns = networks[0]
+ }
- ns, cniNets, err := ParseNetworkNamespace(network, containerConfig.Containers.RootlessNetworking == "cni")
- if err != nil {
- return Namespace{}, nil, nil, err
+ toReturn := Namespace{}
+ podmanNetworks := make(map[string]types.PerNetworkOptions)
+
+ switch {
+ case ns == string(Slirp), strings.HasPrefix(ns, string(Slirp)+":"):
+ parts := strings.SplitN(ns, ":", 2)
+ if len(parts) > 1 {
+ networkOptions = make(map[string][]string)
+ networkOptions[parts[0]] = strings.Split(parts[1], ",")
+ }
+ toReturn.NSMode = Slirp
+ case ns == string(FromPod):
+ toReturn.NSMode = FromPod
+ case ns == "" || ns == string(Default) || ns == string(Private):
+ // Net defaults to Slirp on rootless
+ if rootless.IsRootless() && containerConfig.Containers.RootlessNetworking != "cni" {
+ toReturn.NSMode = Slirp
+ break
+ }
+ // if not slirp we use bridge
+ fallthrough
+ case ns == string(Bridge), strings.HasPrefix(ns, string(Bridge)+":"):
+ toReturn.NSMode = Bridge
+ parts := strings.SplitN(ns, ":", 2)
+ netOpts := types.PerNetworkOptions{}
+ if len(parts) > 1 {
+ var err error
+ netOpts, err = parseBridgeNetworkOptions(parts[1])
+ if err != nil {
+ return toReturn, nil, nil, err
+ }
+ }
+ // we have to set the special default network name here
+ podmanNetworks["default"] = netOpts
+
+ case ns == string(NoNetwork):
+ toReturn.NSMode = NoNetwork
+ case ns == string(Host):
+ toReturn.NSMode = Host
+ case strings.HasPrefix(ns, "ns:"):
+ split := strings.SplitN(ns, ":", 2)
+ if len(split) != 2 {
+ return toReturn, nil, nil, errors.Errorf("must provide a path to a namespace when specifying ns:")
+ }
+ toReturn.NSMode = Path
+ toReturn.Value = split[1]
+ case strings.HasPrefix(ns, string(FromContainer)+":"):
+ split := strings.SplitN(ns, ":", 2)
+ if len(split) != 2 {
+ return toReturn, nil, nil, errors.Errorf("must provide name or ID or a container when specifying container:")
+ }
+ toReturn.NSMode = FromContainer
+ toReturn.Value = split[1]
+ default:
+ // we should have a normal network
+ parts := strings.SplitN(ns, ":", 2)
+ if len(parts) == 1 {
+ // Assume we have been given a comma separated list of networks for backwards compat.
+ networkList := strings.Split(ns, ",")
+ for _, net := range networkList {
+ podmanNetworks[net] = types.PerNetworkOptions{}
+ }
+ } else {
+ if parts[0] == "" {
+ return toReturn, nil, nil, errors.New("network name cannot be empty")
+ }
+ netOpts, err := parseBridgeNetworkOptions(parts[1])
+ if err != nil {
+ return toReturn, nil, nil, errors.Wrapf(err, "invalid option for network %s", parts[0])
+ }
+ podmanNetworks[parts[0]] = netOpts
+ }
+
+ // networks need bridge mode
+ toReturn.NSMode = Bridge
+ }
+
+ if len(networks) > 1 {
+ if !toReturn.IsBridge() {
+ return toReturn, nil, nil, errors.Wrapf(define.ErrInvalidArg, "cannot set multiple networks without bridge network mode, selected mode %s", toReturn.NSMode)
+ }
+
+ for _, network := range networks[1:] {
+ parts := strings.SplitN(network, ":", 2)
+ if parts[0] == "" {
+ return toReturn, nil, nil, errors.Wrapf(define.ErrInvalidArg, "network name cannot be empty")
+ }
+ if util.StringInSlice(parts[0], []string{string(Bridge), string(Slirp), string(FromPod), string(NoNetwork),
+ string(Default), string(Private), string(Path), string(FromContainer), string(Host)}) {
+ return toReturn, nil, nil, errors.Wrapf(define.ErrInvalidArg, "can only set extra network names, selected mode %s conflicts with bridge", parts[0])
+ }
+ netOpts := types.PerNetworkOptions{}
+ if len(parts) > 1 {
+ var err error
+ netOpts, err = parseBridgeNetworkOptions(parts[1])
+ if err != nil {
+ return toReturn, nil, nil, errors.Wrapf(err, "invalid option for network %s", parts[0])
+ }
+ }
+ podmanNetworks[parts[0]] = netOpts
+ }
+ }
+
+ return toReturn, podmanNetworks, networkOptions, nil
+}
+
+func parseBridgeNetworkOptions(opts string) (types.PerNetworkOptions, error) {
+ netOpts := types.PerNetworkOptions{}
+ if len(opts) == 0 {
+ return netOpts, nil
}
+ allopts := strings.Split(opts, ",")
+ for _, opt := range allopts {
+ split := strings.SplitN(opt, "=", 2)
+ switch split[0] {
+ case "ip", "ip6":
+ ip := net.ParseIP(split[1])
+ if ip == nil {
+ return netOpts, errors.Errorf("invalid ip address %q", split[1])
+ }
+ netOpts.StaticIPs = append(netOpts.StaticIPs, ip)
- if len(parts) > 1 {
- networkOptions = make(map[string][]string)
- networkOptions[parts[0]] = strings.Split(parts[1], ",")
- cniNets = nil
+ case "mac":
+ mac, err := net.ParseMAC(split[1])
+ if err != nil {
+ return netOpts, err
+ }
+ netOpts.StaticMAC = types.HardwareAddr(mac)
+
+ case "alias":
+ if split[1] == "" {
+ return netOpts, errors.New("alias cannot be empty")
+ }
+ netOpts.Aliases = append(netOpts.Aliases, split[1])
+
+ case "interface_name":
+ if split[1] == "" {
+ return netOpts, errors.New("interface_name cannot be empty")
+ }
+ netOpts.InterfaceName = split[1]
+
+ default:
+ return netOpts, errors.Errorf("unknown bridge network option: %s", split[0])
+ }
}
- return ns, cniNets, networkOptions, nil
+ return netOpts, nil
}
func SetupUserNS(idmappings *storage.IDMappingOptions, userns Namespace, g *generate.Generator) (string, error) {