1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
// SPDX-License-Identifier: Apache-2.0
//
// pasta.go - Start pasta(1) for user-mode connectivity
//
// Copyright (c) 2022 Red Hat GmbH
// Author: Stefano Brivio <sbrivio@redhat.com>
// This file has been imported from the podman repository
// (libpod/networking_pasta_linux.go), for the full history see there.
package pasta
import (
"errors"
"fmt"
"os/exec"
"strings"
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/config"
"github.com/sirupsen/logrus"
)
const (
BinaryName = "pasta"
)
type SetupOptions struct {
// Config used to get pasta options and binary path via HelperBinariesDir
Config *config.Config
// Netns is the path to the container Netns
Netns string
// Ports that should be forwarded in the container
Ports []types.PortMapping
// ExtraOptions are pasta(1) cli options, these will be appended after the
// pasta options from containers.conf to allow some form of overwrite.
ExtraOptions []string
}
// Setup start the pasta process for the given netns.
// The pasta binary is looked up in the HelperBinariesDir and $PATH.
// Note that there is no need any special cleanup logic, the pasta process will
// automatically exit when the netns path is deleted.
func Setup(opts *SetupOptions) error {
NoTCPInitPorts := true
NoUDPInitPorts := true
NoTCPNamespacePorts := true
NoUDPNamespacePorts := true
NoMapGW := true
path, err := opts.Config.FindHelperBinary(BinaryName, true)
if err != nil {
return fmt.Errorf("could not find pasta, the network namespace can't be configured: %w", err)
}
cmdArgs := []string{}
cmdArgs = append(cmdArgs, "--config-net")
for _, i := range opts.Ports {
protocols := strings.Split(i.Protocol, ",")
for _, protocol := range protocols {
var addr string
if i.HostIP != "" {
addr = fmt.Sprintf("%s/", i.HostIP)
}
switch protocol {
case "tcp":
cmdArgs = append(cmdArgs, "-t")
case "udp":
cmdArgs = append(cmdArgs, "-u")
default:
return fmt.Errorf("can't forward protocol: %s", protocol)
}
arg := fmt.Sprintf("%s%d-%d:%d-%d", addr,
i.HostPort,
i.HostPort+i.Range-1,
i.ContainerPort,
i.ContainerPort+i.Range-1)
cmdArgs = append(cmdArgs, arg)
}
}
// first append options set in the config
cmdArgs = append(cmdArgs, opts.Config.Network.PastaOptions...)
// then append the ones that were set on the cli
cmdArgs = append(cmdArgs, opts.ExtraOptions...)
for i, opt := range cmdArgs {
switch opt {
case "-t", "--tcp-ports":
NoTCPInitPorts = false
case "-u", "--udp-ports":
NoUDPInitPorts = false
case "-T", "--tcp-ns":
NoTCPNamespacePorts = false
case "-U", "--udp-ns":
NoUDPNamespacePorts = false
case "--map-gw":
NoMapGW = false
// not an actual pasta(1) option
cmdArgs = append(cmdArgs[:i], cmdArgs[i+1:]...)
}
}
if NoTCPInitPorts {
cmdArgs = append(cmdArgs, "-t", "none")
}
if NoUDPInitPorts {
cmdArgs = append(cmdArgs, "-u", "none")
}
if NoTCPNamespacePorts {
cmdArgs = append(cmdArgs, "-T", "none")
}
if NoUDPNamespacePorts {
cmdArgs = append(cmdArgs, "-U", "none")
}
if NoMapGW {
cmdArgs = append(cmdArgs, "--no-map-gw")
}
cmdArgs = append(cmdArgs, "--netns", opts.Netns)
logrus.Debugf("pasta arguments: %s", strings.Join(cmdArgs, " "))
// pasta forks once ready, and quits once we delete the target namespace
_, err = exec.Command(path, cmdArgs...).Output()
if err != nil {
exitErr := &exec.ExitError{}
if errors.As(err, &exitErr) {
return fmt.Errorf("pasta failed with exit code %d:\n%s",
exitErr.ExitCode(), exitErr.Stderr)
}
return fmt.Errorf("failed to start pasta: %w", err)
}
return nil
}
|