Skip to content

Commit

Permalink
Merge pull request #1929 from Luap99/backport-0.58
Browse files Browse the repository at this point in the history
[v0.58] Backport pasta fixes
  • Loading branch information
rhatdan authored Mar 25, 2024
2 parents e58240a + 897f762 commit 7d68a8e
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 37 deletions.
15 changes: 12 additions & 3 deletions libnetwork/etchosts/ip.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
package etchosts

import (
"net"

"github.com/containers/common/libnetwork/types"
"github.com/containers/common/libnetwork/util"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/machine"
"github.com/containers/storage/pkg/unshare"
)

// GetHostContainersInternalIP return the host.containers.internal ip
// GetHostContainersInternalIP returns the host.containers.internal ip
// if netStatus is not nil then networkInterface also must be non nil otherwise this function panics
func GetHostContainersInternalIP(conf *config.Config, netStatus map[string]types.StatusBlock, networkInterface types.ContainerNetwork) string {
return GetHostContainersInternalIPExcluding(conf, netStatus, networkInterface, nil)
}

// GetHostContainersInternalIPExcluding returns the host.containers.internal ip
// Exclude are ips that should not be returned, this is useful to prevent returning the same ip as in the container.
// if netStatus is not nil then networkInterface also must be non nil otherwise this function panics
func GetHostContainersInternalIPExcluding(conf *config.Config, netStatus map[string]types.StatusBlock, networkInterface types.ContainerNetwork, exclude []net.IP) string {
switch conf.Containers.HostContainersInternalIP {
case "":
// if empty (default) we will automatically choose one below
Expand All @@ -27,7 +36,7 @@ func GetHostContainersInternalIP(conf *config.Config, netStatus map[string]types
// Only use the bridge ip when root, as rootless the interfaces are created
// inside the special netns and not the host so we cannot use them.
if unshare.IsRootless() {
return util.GetLocalIP()
return util.GetLocalIPExcluding(exclude)
}
for net, status := range netStatus {
network, err := networkInterface.NetworkInspect(net)
Expand All @@ -51,7 +60,7 @@ func GetHostContainersInternalIP(conf *config.Config, netStatus map[string]types
if ip != "" {
return ip
}
return util.GetLocalIP()
return util.GetLocalIPExcluding(exclude)
}

// GetNetworkHostEntries returns HostEntries for all ips in the network status
Expand Down
9 changes: 4 additions & 5 deletions libnetwork/internal/rootlessnetns/netns_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ func (n *Netns) setupPasta(nsPath string) error {
Netns: nsPath,
ExtraOptions: []string{"--pid", pidPath},
}
if err := pasta.Setup(&pastaOpts); err != nil {
res, err := pasta.Setup2(&pastaOpts)
if err != nil {
return fmt.Errorf("setting up Pasta: %w", err)
}

Expand All @@ -185,11 +186,9 @@ func (n *Netns) setupPasta(nsPath string) error {
Namespaces: []specs.LinuxNamespace{
{Type: specs.NetworkNamespace},
},
// TODO: Need a way to determine if there is a valid v6 address on any
// external interface of the system.
IPv6Enabled: false,
IPv6Enabled: res.IPv6,
KeepHostServers: true,
Nameservers: []string{},
Nameservers: res.DNSForwardIPs,
}); err != nil {
return wrapError("create resolv.conf", err)
}
Expand Down
87 changes: 68 additions & 19 deletions libnetwork/pasta/pasta.go → libnetwork/pasta/pasta_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,23 @@ package pasta
import (
"errors"
"fmt"
"net"
"os/exec"
"strings"

"github.com/containernetworking/plugins/pkg/ns"
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/libnetwork/util"
"github.com/containers/common/pkg/config"
"github.com/sirupsen/logrus"
)

const (
BinaryName = "pasta"
dnsForwardOpt = "--dns-forward"

// dnsForwardIpv4 static ip used as nameserver address inside the netns,
// given this is a "link local" ip it should be very unlikely that it causes conflicts
dnsForwardIpv4 = "169.254.0.1"
)

type SetupOptions struct {
Expand All @@ -37,21 +44,25 @@ type SetupOptions struct {
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 {
_, err := Setup2(opts)
return err
}

// Setup2 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 for any special cleanup logic, the pasta
// process will automatically exit when the netns path is deleted.
func Setup2(opts *SetupOptions) (*SetupResult, error) {
NoTCPInitPorts := true
NoUDPInitPorts := true
NoTCPNamespacePorts := true
NoUDPNamespacePorts := true
NoMapGW := true
NoDNS := 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)
return nil, fmt.Errorf("could not find pasta, the network namespace can't be configured: %w", err)
}

cmdArgs := []string{}
Expand All @@ -72,7 +83,7 @@ func Setup(opts *SetupOptions) error {
case "udp":
cmdArgs = append(cmdArgs, "-u")
default:
return fmt.Errorf("can't forward protocol: %s", protocol)
return nil, fmt.Errorf("can't forward protocol: %s", protocol)
}

arg := fmt.Sprintf("%s%d-%d:%d-%d", addr,
Expand All @@ -89,6 +100,7 @@ func Setup(opts *SetupOptions) error {
// then append the ones that were set on the cli
cmdArgs = append(cmdArgs, opts.ExtraOptions...)

var dnsForwardIPs []string
for i, opt := range cmdArgs {
switch opt {
case "-t", "--tcp-ports":
Expand All @@ -103,11 +115,20 @@ func Setup(opts *SetupOptions) error {
NoMapGW = false
// not an actual pasta(1) option
cmdArgs = append(cmdArgs[:i], cmdArgs[i+1:]...)
case "-D", "--dns", "--dns-forward":
NoDNS = false
case dnsForwardOpt:
// if there is no arg after it pasta will likely error out anyway due invalid cli args
if len(cmdArgs) > i+1 {
dnsForwardIPs = append(dnsForwardIPs, cmdArgs[i+1])
}
}
}

if len(dnsForwardIPs) == 0 {
// the user did not request custom --dns-forward so add our own.
cmdArgs = append(cmdArgs, dnsForwardOpt, dnsForwardIpv4)
dnsForwardIPs = append(dnsForwardIPs, dnsForwardIpv4)
}

if NoTCPInitPorts {
cmdArgs = append(cmdArgs, "-t", "none")
}
Expand All @@ -123,12 +144,6 @@ func Setup(opts *SetupOptions) error {
if NoMapGW {
cmdArgs = append(cmdArgs, "--no-map-gw")
}
if NoDNS {
// disable pasta reading from /etc/resolv.conf which hides the
// "Couldn't get any nameserver address" warning when only
// localhost resolvers are configured.
cmdArgs = append(cmdArgs, "--dns", "none")
}

// always pass --quiet to silence the info output from pasta
cmdArgs = append(cmdArgs, "--quiet", "--netns", opts.Netns)
Expand All @@ -140,10 +155,10 @@ func Setup(opts *SetupOptions) error {
if err != nil {
exitErr := &exec.ExitError{}
if errors.As(err, &exitErr) {
return fmt.Errorf("pasta failed with exit code %d:\n%s",
return nil, fmt.Errorf("pasta failed with exit code %d:\n%s",
exitErr.ExitCode(), string(out))
}
return fmt.Errorf("failed to start pasta: %w", err)
return nil, fmt.Errorf("failed to start pasta: %w", err)
}

if len(out) > 0 {
Expand All @@ -154,5 +169,39 @@ func Setup(opts *SetupOptions) error {
logrus.Infof("pasta logged warnings: %q", string(out))
}

return nil
var ipv4, ipv6 bool
result := &SetupResult{}
err = ns.WithNetNSPath(opts.Netns, func(_ ns.NetNS) error {
addrs, err := net.InterfaceAddrs()
if err != nil {
return err
}
for _, addr := range addrs {
// make sure to skip localhost and other special addresses
if ipnet, ok := addr.(*net.IPNet); ok && ipnet.IP.IsGlobalUnicast() {
result.IPAddresses = append(result.IPAddresses, ipnet.IP)
if !ipv4 && util.IsIPv4(ipnet.IP) {
ipv4 = true
}
if !ipv6 && util.IsIPv6(ipnet.IP) {
ipv6 = true
}
}
}
return nil
})
if err != nil {
return nil, err
}

result.IPv6 = ipv6
for _, ip := range dnsForwardIPs {
ipp := net.ParseIP(ip)
// add the namesever ip only if the address family matches
if ipv4 && util.IsIPv4(ipp) || ipv6 && util.IsIPv6(ipp) {
result.DNSForwardIPs = append(result.DNSForwardIPs, ip)
}
}

return result, nil
}
15 changes: 15 additions & 0 deletions libnetwork/pasta/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package pasta

import "net"

const BinaryName = "pasta"

type SetupResult struct {
// IpAddresses configured by pasta
IPAddresses []net.IP
// DNSForwardIP is the ip used in --dns-forward, it should be added as first
// entry to resolv.conf in the container.
DNSForwardIPs []string
// IPv6 says whenever pasta run with ipv6 support
IPv6 bool
}
12 changes: 12 additions & 0 deletions libnetwork/slirp4netns/const.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package slirp4netns

import "net"

const (
ipv6ConfDefaultAcceptDadSysctl = "/proc/sys/net/ipv6/conf/default/accept_dad"
BinaryName = "slirp4netns"
Expand All @@ -10,3 +12,13 @@ const (
// default slirp4ns subnet
defaultSubnet = "10.0.2.0/24"
)

// SetupResult return type from Setup()
type SetupResult struct {
// Pid of the created slirp4netns process
Pid int
// Subnet which is used by slirp4netns
Subnet *net.IPNet
// IPv6 whenever Ipv6 is enabled in slirp4netns
IPv6 bool
}
10 changes: 0 additions & 10 deletions libnetwork/slirp4netns/slirp4netns.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,6 @@ type SetupOptions struct {
Pdeathsig syscall.Signal
}

// SetupResult return type from Setup()
type SetupResult struct {
// Pid of the created slirp4netns process
Pid int
// Subnet which is used by slirp4netns
Subnet *net.IPNet
// IPv6 whenever Ipv6 is enabled in slirp4netns
IPv6 bool
}

type logrusDebugWriter struct {
prefix string
}
Expand Down
17 changes: 17 additions & 0 deletions libnetwork/util/ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,31 @@ func NormalizeIP(ip *net.IP) {
// If no ipv4 address is found it may return an ipv6 address.
// When no ip is found and empty string is returned.
func GetLocalIP() string {
return GetLocalIPExcluding(nil)
}

// GetLocalIPExcluding returns the first non loopback local IPv4 of the host.
// If no ipv4 address is found it may return an ipv6 address.
// Additionally you can specify a list of ips that should not be returned.
// When no ip is found and empty string is returned.
func GetLocalIPExcluding(exclude []net.IP) string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return ""
}
ip := ""
outer:
for _, address := range addrs {
// check the address type and if it is not a loopback the display it
if ipnet, ok := address.(*net.IPNet); ok && ipnet.IP.IsGlobalUnicast() {
// cannot use slices.Contains for net.IP
for _, eip := range exclude {
if eip.Equal(ipnet.IP) {
// ip should be excluded skip to next one
continue outer
}
}

if IsIPv4(ipnet.IP) {
return ipnet.IP.String()
}
Expand Down

0 comments on commit 7d68a8e

Please sign in to comment.