github.com/AbhinandanKurakure/podman/v3@v3.4.10/libpod/networking_slirp4netns.go (about)

     1  // +build linux
     2  
     3  package libpod
     4  
     5  import (
     6  	"bytes"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"net"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"strconv"
    15  	"strings"
    16  	"syscall"
    17  	"time"
    18  
    19  	"github.com/containernetworking/plugins/pkg/ns"
    20  	"github.com/containers/podman/v3/pkg/errorhandling"
    21  	"github.com/containers/podman/v3/pkg/rootless"
    22  	"github.com/containers/podman/v3/pkg/rootlessport"
    23  	"github.com/containers/podman/v3/pkg/servicereaper"
    24  	"github.com/pkg/errors"
    25  	"github.com/sirupsen/logrus"
    26  )
    27  
    28  type slirpFeatures struct {
    29  	HasDisableHostLoopback bool
    30  	HasMTU                 bool
    31  	HasEnableSandbox       bool
    32  	HasEnableSeccomp       bool
    33  	HasCIDR                bool
    34  	HasOutboundAddr        bool
    35  	HasIPv6                bool
    36  }
    37  
    38  type slirp4netnsCmdArg struct {
    39  	Proto     string `json:"proto,omitempty"`
    40  	HostAddr  string `json:"host_addr"`
    41  	HostPort  int32  `json:"host_port"`
    42  	GuestAddr string `json:"guest_addr"`
    43  	GuestPort int32  `json:"guest_port"`
    44  }
    45  
    46  type slirp4netnsCmd struct {
    47  	Execute string            `json:"execute"`
    48  	Args    slirp4netnsCmdArg `json:"arguments"`
    49  }
    50  
    51  type slirp4netnsNetworkOptions struct {
    52  	cidr                string
    53  	disableHostLoopback bool
    54  	enableIPv6          bool
    55  	isSlirpHostForward  bool
    56  	noPivotRoot         bool
    57  	mtu                 int
    58  	outboundAddr        string
    59  	outboundAddr6       string
    60  }
    61  
    62  const ipv6ConfDefaultAcceptDadSysctl = "/proc/sys/net/ipv6/conf/default/accept_dad"
    63  
    64  func checkSlirpFlags(path string) (*slirpFeatures, error) {
    65  	cmd := exec.Command(path, "--help")
    66  	out, err := cmd.CombinedOutput()
    67  	if err != nil {
    68  		return nil, errors.Wrapf(err, "slirp4netns %q", out)
    69  	}
    70  	return &slirpFeatures{
    71  		HasDisableHostLoopback: strings.Contains(string(out), "--disable-host-loopback"),
    72  		HasMTU:                 strings.Contains(string(out), "--mtu"),
    73  		HasEnableSandbox:       strings.Contains(string(out), "--enable-sandbox"),
    74  		HasEnableSeccomp:       strings.Contains(string(out), "--enable-seccomp"),
    75  		HasCIDR:                strings.Contains(string(out), "--cidr"),
    76  		HasOutboundAddr:        strings.Contains(string(out), "--outbound-addr"),
    77  		HasIPv6:                strings.Contains(string(out), "--enable-ipv6"),
    78  	}, nil
    79  }
    80  
    81  func parseSlirp4netnsNetworkOptions(r *Runtime, extraOptions []string) (*slirp4netnsNetworkOptions, error) {
    82  	slirpOptions := append(r.config.Engine.NetworkCmdOptions, extraOptions...)
    83  	slirp4netnsOpts := &slirp4netnsNetworkOptions{
    84  		// overwrite defaults
    85  		disableHostLoopback: true,
    86  		mtu:                 slirp4netnsMTU,
    87  		noPivotRoot:         r.config.Engine.NoPivotRoot,
    88  	}
    89  	for _, o := range slirpOptions {
    90  		parts := strings.SplitN(o, "=", 2)
    91  		if len(parts) < 2 {
    92  			return nil, errors.Errorf("unknown option for slirp4netns: %q", o)
    93  		}
    94  		option, value := parts[0], parts[1]
    95  		switch option {
    96  		case "cidr":
    97  			ipv4, _, err := net.ParseCIDR(value)
    98  			if err != nil || ipv4.To4() == nil {
    99  				return nil, errors.Errorf("invalid cidr %q", value)
   100  			}
   101  			slirp4netnsOpts.cidr = value
   102  		case "port_handler":
   103  			switch value {
   104  			case "slirp4netns":
   105  				slirp4netnsOpts.isSlirpHostForward = true
   106  			case "rootlesskit":
   107  				slirp4netnsOpts.isSlirpHostForward = false
   108  			default:
   109  				return nil, errors.Errorf("unknown port_handler for slirp4netns: %q", value)
   110  			}
   111  		case "allow_host_loopback":
   112  			switch value {
   113  			case "true":
   114  				slirp4netnsOpts.disableHostLoopback = false
   115  			case "false":
   116  				slirp4netnsOpts.disableHostLoopback = true
   117  			default:
   118  				return nil, errors.Errorf("invalid value of allow_host_loopback for slirp4netns: %q", value)
   119  			}
   120  		case "enable_ipv6":
   121  			switch value {
   122  			case "true":
   123  				slirp4netnsOpts.enableIPv6 = true
   124  			case "false":
   125  				slirp4netnsOpts.enableIPv6 = false
   126  			default:
   127  				return nil, errors.Errorf("invalid value of enable_ipv6 for slirp4netns: %q", value)
   128  			}
   129  		case "outbound_addr":
   130  			ipv4 := net.ParseIP(value)
   131  			if ipv4 == nil || ipv4.To4() == nil {
   132  				_, err := net.InterfaceByName(value)
   133  				if err != nil {
   134  					return nil, errors.Errorf("invalid outbound_addr %q", value)
   135  				}
   136  			}
   137  			slirp4netnsOpts.outboundAddr = value
   138  		case "outbound_addr6":
   139  			ipv6 := net.ParseIP(value)
   140  			if ipv6 == nil || ipv6.To4() != nil {
   141  				_, err := net.InterfaceByName(value)
   142  				if err != nil {
   143  					return nil, errors.Errorf("invalid outbound_addr6: %q", value)
   144  				}
   145  			}
   146  			slirp4netnsOpts.outboundAddr6 = value
   147  		case "mtu":
   148  			var err error
   149  			slirp4netnsOpts.mtu, err = strconv.Atoi(value)
   150  			if slirp4netnsOpts.mtu < 68 || err != nil {
   151  				return nil, errors.Errorf("invalid mtu %q", value)
   152  			}
   153  		default:
   154  			return nil, errors.Errorf("unknown option for slirp4netns: %q", o)
   155  		}
   156  	}
   157  	return slirp4netnsOpts, nil
   158  }
   159  
   160  func createBasicSlirp4netnsCmdArgs(options *slirp4netnsNetworkOptions, features *slirpFeatures) ([]string, error) {
   161  	cmdArgs := []string{}
   162  	if options.disableHostLoopback && features.HasDisableHostLoopback {
   163  		cmdArgs = append(cmdArgs, "--disable-host-loopback")
   164  	}
   165  	if options.mtu > -1 && features.HasMTU {
   166  		cmdArgs = append(cmdArgs, fmt.Sprintf("--mtu=%d", options.mtu))
   167  	}
   168  	if !options.noPivotRoot && features.HasEnableSandbox {
   169  		cmdArgs = append(cmdArgs, "--enable-sandbox")
   170  	}
   171  	if features.HasEnableSeccomp {
   172  		cmdArgs = append(cmdArgs, "--enable-seccomp")
   173  	}
   174  
   175  	if options.cidr != "" {
   176  		if !features.HasCIDR {
   177  			return nil, errors.Errorf("cidr not supported")
   178  		}
   179  		cmdArgs = append(cmdArgs, fmt.Sprintf("--cidr=%s", options.cidr))
   180  	}
   181  
   182  	if options.enableIPv6 {
   183  		if !features.HasIPv6 {
   184  			return nil, errors.Errorf("enable_ipv6 not supported")
   185  		}
   186  		cmdArgs = append(cmdArgs, "--enable-ipv6")
   187  	}
   188  
   189  	if options.outboundAddr != "" {
   190  		if !features.HasOutboundAddr {
   191  			return nil, errors.Errorf("outbound_addr not supported")
   192  		}
   193  		cmdArgs = append(cmdArgs, fmt.Sprintf("--outbound-addr=%s", options.outboundAddr))
   194  	}
   195  
   196  	if options.outboundAddr6 != "" {
   197  		if !features.HasOutboundAddr || !features.HasIPv6 {
   198  			return nil, errors.Errorf("outbound_addr6 not supported")
   199  		}
   200  		if !options.enableIPv6 {
   201  			return nil, errors.Errorf("enable_ipv6=true is required for outbound_addr6")
   202  		}
   203  		cmdArgs = append(cmdArgs, fmt.Sprintf("--outbound-addr6=%s", options.outboundAddr6))
   204  	}
   205  
   206  	return cmdArgs, nil
   207  }
   208  
   209  // setupSlirp4netns can be called in rootful as well as in rootless
   210  func (r *Runtime) setupSlirp4netns(ctr *Container) error {
   211  	path := r.config.Engine.NetworkCmdPath
   212  	if path == "" {
   213  		var err error
   214  		path, err = exec.LookPath("slirp4netns")
   215  		if err != nil {
   216  			logrus.Errorf("could not find slirp4netns, the network namespace won't be configured: %v", err)
   217  			return nil
   218  		}
   219  	}
   220  
   221  	syncR, syncW, err := os.Pipe()
   222  	if err != nil {
   223  		return errors.Wrapf(err, "failed to open pipe")
   224  	}
   225  	defer errorhandling.CloseQuiet(syncR)
   226  	defer errorhandling.CloseQuiet(syncW)
   227  
   228  	havePortMapping := len(ctr.config.PortMappings) > 0
   229  	logPath := filepath.Join(ctr.runtime.config.Engine.TmpDir, fmt.Sprintf("slirp4netns-%s.log", ctr.config.ID))
   230  
   231  	ctrNetworkSlipOpts := []string{}
   232  	if ctr.config.NetworkOptions != nil {
   233  		ctrNetworkSlipOpts = append(ctrNetworkSlipOpts, ctr.config.NetworkOptions["slirp4netns"]...)
   234  	}
   235  	netOptions, err := parseSlirp4netnsNetworkOptions(r, ctrNetworkSlipOpts)
   236  	if err != nil {
   237  		return err
   238  	}
   239  	slirpFeatures, err := checkSlirpFlags(path)
   240  	if err != nil {
   241  		return errors.Wrapf(err, "error checking slirp4netns binary %s: %q", path, err)
   242  	}
   243  	cmdArgs, err := createBasicSlirp4netnsCmdArgs(netOptions, slirpFeatures)
   244  	if err != nil {
   245  		return err
   246  	}
   247  
   248  	// the slirp4netns arguments being passed are describes as follows:
   249  	// from the slirp4netns documentation: https://github.com/rootless-containers/slirp4netns
   250  	// -c, --configure Brings up the tap interface
   251  	// -e, --exit-fd=FD specify the FD for terminating slirp4netns
   252  	// -r, --ready-fd=FD specify the FD to write to when the initialization steps are finished
   253  	cmdArgs = append(cmdArgs, "-c", "-e", "3", "-r", "4")
   254  
   255  	var apiSocket string
   256  	if havePortMapping && netOptions.isSlirpHostForward {
   257  		apiSocket = filepath.Join(ctr.runtime.config.Engine.TmpDir, fmt.Sprintf("%s.net", ctr.config.ID))
   258  		cmdArgs = append(cmdArgs, "--api-socket", apiSocket)
   259  	}
   260  	netnsPath := ""
   261  	if !ctr.config.PostConfigureNetNS {
   262  		ctr.rootlessSlirpSyncR, ctr.rootlessSlirpSyncW, err = os.Pipe()
   263  		if err != nil {
   264  			return errors.Wrapf(err, "failed to create rootless network sync pipe")
   265  		}
   266  		netnsPath = ctr.state.NetNS.Path()
   267  		cmdArgs = append(cmdArgs, "--netns-type=path", netnsPath, "tap0")
   268  	} else {
   269  		defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncR)
   270  		defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncW)
   271  		netnsPath = fmt.Sprintf("/proc/%d/ns/net", ctr.state.PID)
   272  		// we don't use --netns-path here (unavailable for slirp4netns < v0.4)
   273  		cmdArgs = append(cmdArgs, fmt.Sprintf("%d", ctr.state.PID), "tap0")
   274  	}
   275  
   276  	cmd := exec.Command(path, cmdArgs...)
   277  	logrus.Debugf("slirp4netns command: %s", strings.Join(cmd.Args, " "))
   278  	cmd.SysProcAttr = &syscall.SysProcAttr{
   279  		Setpgid: true,
   280  	}
   281  
   282  	// workaround for https://github.com/rootless-containers/slirp4netns/pull/153
   283  	if !netOptions.noPivotRoot && slirpFeatures.HasEnableSandbox {
   284  		cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNS
   285  		cmd.SysProcAttr.Unshareflags = syscall.CLONE_NEWNS
   286  	}
   287  
   288  	// Leak one end of the pipe in slirp4netns, the other will be sent to conmon
   289  	cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.rootlessSlirpSyncR, syncW)
   290  
   291  	logFile, err := os.Create(logPath)
   292  	if err != nil {
   293  		return errors.Wrapf(err, "failed to open slirp4netns log file %s", logPath)
   294  	}
   295  	defer logFile.Close()
   296  	// Unlink immediately the file so we won't need to worry about cleaning it up later.
   297  	// It is still accessible through the open fd logFile.
   298  	if err := os.Remove(logPath); err != nil {
   299  		return errors.Wrapf(err, "delete file %s", logPath)
   300  	}
   301  	cmd.Stdout = logFile
   302  	cmd.Stderr = logFile
   303  
   304  	var slirpReadyChan (chan struct{})
   305  
   306  	if netOptions.enableIPv6 {
   307  		slirpReadyChan = make(chan struct{})
   308  		defer close(slirpReadyChan)
   309  		go func() {
   310  			err := ns.WithNetNSPath(netnsPath, func(_ ns.NetNS) error {
   311  				// Duplicate Address Detection slows the ipv6 setup down for 1-2 seconds.
   312  				// Since slirp4netns is run it is own namespace and not directly routed
   313  				// we can skip this to make the ipv6 address immediately available.
   314  				// We change the default to make sure the slirp tap interface gets the
   315  				// correct value assigned so DAD is disabled for it
   316  				// Also make sure to change this value back to the original after slirp4netns
   317  				// is ready in case users rely on this sysctl.
   318  				orgValue, err := ioutil.ReadFile(ipv6ConfDefaultAcceptDadSysctl)
   319  				if err != nil {
   320  					return err
   321  				}
   322  				err = ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, []byte("0"), 0644)
   323  				if err != nil {
   324  					return err
   325  				}
   326  				// wait for slirp to finish setup
   327  				<-slirpReadyChan
   328  				return ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, orgValue, 0644)
   329  			})
   330  			if err != nil {
   331  				logrus.Warnf("failed to set net.ipv6.conf.default.accept_dad sysctl: %v", err)
   332  			}
   333  		}()
   334  	}
   335  
   336  	if err := cmd.Start(); err != nil {
   337  		return errors.Wrapf(err, "failed to start slirp4netns process")
   338  	}
   339  	defer func() {
   340  		servicereaper.AddPID(cmd.Process.Pid)
   341  		if err := cmd.Process.Release(); err != nil {
   342  			logrus.Errorf("unable to release command process: %q", err)
   343  		}
   344  	}()
   345  
   346  	if err := waitForSync(syncR, cmd, logFile, 1*time.Second); err != nil {
   347  		return err
   348  	}
   349  	if slirpReadyChan != nil {
   350  		slirpReadyChan <- struct{}{}
   351  	}
   352  
   353  	// Set a default slirp subnet. Parsing a string with the net helper is easier than building the struct myself
   354  	_, ctr.slirp4netnsSubnet, _ = net.ParseCIDR(defaultSlirp4netnsSubnet)
   355  
   356  	// Set slirp4netnsSubnet addresses now that we are pretty sure the command executed
   357  	if netOptions.cidr != "" {
   358  		ipv4, ipv4network, err := net.ParseCIDR(netOptions.cidr)
   359  		if err != nil || ipv4.To4() == nil {
   360  			return errors.Errorf("invalid cidr %q", netOptions.cidr)
   361  		}
   362  		ctr.slirp4netnsSubnet = ipv4network
   363  	}
   364  
   365  	if havePortMapping {
   366  		if netOptions.isSlirpHostForward {
   367  			return r.setupRootlessPortMappingViaSlirp(ctr, cmd, apiSocket)
   368  		}
   369  		return r.setupRootlessPortMappingViaRLK(ctr, netnsPath)
   370  	}
   371  
   372  	return nil
   373  }
   374  
   375  // Get expected slirp ipv4 address based on subnet. If subnet is null use default subnet
   376  // Reference: https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md#description
   377  func GetSlirp4netnsIP(subnet *net.IPNet) (*net.IP, error) {
   378  	_, slirpSubnet, _ := net.ParseCIDR(defaultSlirp4netnsSubnet)
   379  	if subnet != nil {
   380  		slirpSubnet = subnet
   381  	}
   382  	expectedIP, err := addToIP(slirpSubnet, uint32(100))
   383  	if err != nil {
   384  		return nil, errors.Wrapf(err, "error calculating expected ip for slirp4netns")
   385  	}
   386  	return expectedIP, nil
   387  }
   388  
   389  // Get expected slirp Gateway ipv4 address based on subnet
   390  // Reference: https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md#description
   391  func GetSlirp4netnsGateway(subnet *net.IPNet) (*net.IP, error) {
   392  	_, slirpSubnet, _ := net.ParseCIDR(defaultSlirp4netnsSubnet)
   393  	if subnet != nil {
   394  		slirpSubnet = subnet
   395  	}
   396  	expectedGatewayIP, err := addToIP(slirpSubnet, uint32(2))
   397  	if err != nil {
   398  		return nil, errors.Wrapf(err, "error calculating expected gateway ip for slirp4netns")
   399  	}
   400  	return expectedGatewayIP, nil
   401  }
   402  
   403  // Get expected slirp DNS ipv4 address based on subnet
   404  // Reference: https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md#description
   405  func GetSlirp4netnsDNS(subnet *net.IPNet) (*net.IP, error) {
   406  	_, slirpSubnet, _ := net.ParseCIDR(defaultSlirp4netnsSubnet)
   407  	if subnet != nil {
   408  		slirpSubnet = subnet
   409  	}
   410  	expectedDNSIP, err := addToIP(slirpSubnet, uint32(3))
   411  	if err != nil {
   412  		return nil, errors.Wrapf(err, "error calculating expected dns ip for slirp4netns")
   413  	}
   414  	return expectedDNSIP, nil
   415  }
   416  
   417  // Helper function to calculate slirp ip address offsets
   418  // Adapted from: https://github.com/signalsciences/ipv4/blob/master/int.go#L12-L24
   419  func addToIP(subnet *net.IPNet, offset uint32) (*net.IP, error) {
   420  	// I have no idea why I have to do this, but if I don't ip is 0
   421  	ipFixed := subnet.IP.To4()
   422  
   423  	ipInteger := uint32(ipFixed[3]) | uint32(ipFixed[2])<<8 | uint32(ipFixed[1])<<16 | uint32(ipFixed[0])<<24
   424  	ipNewRaw := ipInteger + offset
   425  	// Avoid overflows
   426  	if ipNewRaw < ipInteger {
   427  		return nil, errors.Errorf("integer overflow while calculating ip address offset, %s + %d", ipFixed, offset)
   428  	}
   429  	ipNew := net.IPv4(byte(ipNewRaw>>24), byte(ipNewRaw>>16&0xFF), byte(ipNewRaw>>8)&0xFF, byte(ipNewRaw&0xFF))
   430  	if !subnet.Contains(ipNew) {
   431  		return nil, errors.Errorf("calculated ip address %s is not within given subnet %s", ipNew.String(), subnet.String())
   432  	}
   433  	return &ipNew, nil
   434  }
   435  
   436  func waitForSync(syncR *os.File, cmd *exec.Cmd, logFile io.ReadSeeker, timeout time.Duration) error {
   437  	prog := filepath.Base(cmd.Path)
   438  	if len(cmd.Args) > 0 {
   439  		prog = cmd.Args[0]
   440  	}
   441  	b := make([]byte, 16)
   442  	for {
   443  		if err := syncR.SetDeadline(time.Now().Add(timeout)); err != nil {
   444  			return errors.Wrapf(err, "error setting %s pipe timeout", prog)
   445  		}
   446  		// FIXME: return err as soon as proc exits, without waiting for timeout
   447  		if _, err := syncR.Read(b); err == nil {
   448  			break
   449  		} else {
   450  			if os.IsTimeout(err) {
   451  				// Check if the process is still running.
   452  				var status syscall.WaitStatus
   453  				pid, err := syscall.Wait4(cmd.Process.Pid, &status, syscall.WNOHANG, nil)
   454  				if err != nil {
   455  					return errors.Wrapf(err, "failed to read %s process status", prog)
   456  				}
   457  				if pid != cmd.Process.Pid {
   458  					continue
   459  				}
   460  				if status.Exited() {
   461  					// Seek at the beginning of the file and read all its content
   462  					if _, err := logFile.Seek(0, 0); err != nil {
   463  						logrus.Errorf("could not seek log file: %q", err)
   464  					}
   465  					logContent, err := ioutil.ReadAll(logFile)
   466  					if err != nil {
   467  						return errors.Wrapf(err, "%s failed", prog)
   468  					}
   469  					return errors.Errorf("%s failed: %q", prog, logContent)
   470  				}
   471  				if status.Signaled() {
   472  					return errors.Errorf("%s killed by signal", prog)
   473  				}
   474  				continue
   475  			}
   476  			return errors.Wrapf(err, "failed to read from %s sync pipe", prog)
   477  		}
   478  	}
   479  	return nil
   480  }
   481  
   482  func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath string) error {
   483  	syncR, syncW, err := os.Pipe()
   484  	if err != nil {
   485  		return errors.Wrapf(err, "failed to open pipe")
   486  	}
   487  	defer errorhandling.CloseQuiet(syncR)
   488  	defer errorhandling.CloseQuiet(syncW)
   489  
   490  	logPath := filepath.Join(ctr.runtime.config.Engine.TmpDir, fmt.Sprintf("rootlessport-%s.log", ctr.config.ID))
   491  	logFile, err := os.Create(logPath)
   492  	if err != nil {
   493  		return errors.Wrapf(err, "failed to open rootlessport log file %s", logPath)
   494  	}
   495  	defer logFile.Close()
   496  	// Unlink immediately the file so we won't need to worry about cleaning it up later.
   497  	// It is still accessible through the open fd logFile.
   498  	if err := os.Remove(logPath); err != nil {
   499  		return errors.Wrapf(err, "delete file %s", logPath)
   500  	}
   501  
   502  	if !ctr.config.PostConfigureNetNS {
   503  		ctr.rootlessPortSyncR, ctr.rootlessPortSyncW, err = os.Pipe()
   504  		if err != nil {
   505  			return errors.Wrapf(err, "failed to create rootless port sync pipe")
   506  		}
   507  	}
   508  
   509  	childIP := getRootlessPortChildIP(ctr)
   510  	cfg := rootlessport.Config{
   511  		Mappings:    ctr.config.PortMappings,
   512  		NetNSPath:   netnsPath,
   513  		ExitFD:      3,
   514  		ReadyFD:     4,
   515  		TmpDir:      ctr.runtime.config.Engine.TmpDir,
   516  		ChildIP:     childIP,
   517  		ContainerID: ctr.config.ID,
   518  		RootlessCNI: ctr.config.NetMode.IsBridge() && rootless.IsRootless(),
   519  	}
   520  	cfgJSON, err := json.Marshal(cfg)
   521  	if err != nil {
   522  		return err
   523  	}
   524  	cfgR := bytes.NewReader(cfgJSON)
   525  	var stdout bytes.Buffer
   526  	cmd := exec.Command(fmt.Sprintf("/proc/%d/exe", os.Getpid()))
   527  	cmd.Args = []string{rootlessport.ReexecKey}
   528  	// Leak one end of the pipe in rootlessport process, the other will be sent to conmon
   529  
   530  	if ctr.rootlessPortSyncR != nil {
   531  		defer errorhandling.CloseQuiet(ctr.rootlessPortSyncR)
   532  	}
   533  
   534  	cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.rootlessPortSyncR, syncW)
   535  	cmd.Stdin = cfgR
   536  	// stdout is for human-readable error, stderr is for debug log
   537  	cmd.Stdout = &stdout
   538  	cmd.Stderr = io.MultiWriter(logFile, &logrusDebugWriter{"rootlessport: "})
   539  	cmd.SysProcAttr = &syscall.SysProcAttr{
   540  		Setpgid: true,
   541  	}
   542  	if err := cmd.Start(); err != nil {
   543  		return errors.Wrapf(err, "failed to start rootlessport process")
   544  	}
   545  	defer func() {
   546  		servicereaper.AddPID(cmd.Process.Pid)
   547  		if err := cmd.Process.Release(); err != nil {
   548  			logrus.Errorf("unable to release rootlessport process: %q", err)
   549  		}
   550  	}()
   551  	if err := waitForSync(syncR, cmd, logFile, 3*time.Second); err != nil {
   552  		stdoutStr := stdout.String()
   553  		if stdoutStr != "" {
   554  			// err contains full debug log and too verbose, so return stdoutStr
   555  			logrus.Debug(err)
   556  			return errors.Errorf("rootlessport " + strings.TrimSuffix(stdoutStr, "\n"))
   557  		}
   558  		return err
   559  	}
   560  	logrus.Debug("rootlessport is ready")
   561  	return nil
   562  }
   563  
   564  func (r *Runtime) setupRootlessPortMappingViaSlirp(ctr *Container, cmd *exec.Cmd, apiSocket string) (err error) {
   565  	const pidWaitTimeout = 60 * time.Second
   566  	chWait := make(chan error)
   567  	go func() {
   568  		interval := 25 * time.Millisecond
   569  		for i := time.Duration(0); i < pidWaitTimeout; i += interval {
   570  			// Check if the process is still running.
   571  			var status syscall.WaitStatus
   572  			pid, err := syscall.Wait4(cmd.Process.Pid, &status, syscall.WNOHANG, nil)
   573  			if err != nil {
   574  				break
   575  			}
   576  			if pid != cmd.Process.Pid {
   577  				continue
   578  			}
   579  			if status.Exited() || status.Signaled() {
   580  				chWait <- fmt.Errorf("slirp4netns exited with status %d", status.ExitStatus())
   581  			}
   582  			time.Sleep(interval)
   583  		}
   584  	}()
   585  	defer close(chWait)
   586  
   587  	// wait that API socket file appears before trying to use it.
   588  	if _, err := WaitForFile(apiSocket, chWait, pidWaitTimeout); err != nil {
   589  		return errors.Wrapf(err, "waiting for slirp4nets to create the api socket file %s", apiSocket)
   590  	}
   591  
   592  	// for each port we want to add we need to open a connection to the slirp4netns control socket
   593  	// and send the add_hostfwd command.
   594  	for _, i := range ctr.config.PortMappings {
   595  		conn, err := net.Dial("unix", apiSocket)
   596  		if err != nil {
   597  			return errors.Wrapf(err, "cannot open connection to %s", apiSocket)
   598  		}
   599  		defer func() {
   600  			if err := conn.Close(); err != nil {
   601  				logrus.Errorf("unable to close connection: %q", err)
   602  			}
   603  		}()
   604  		hostIP := i.HostIP
   605  		if hostIP == "" {
   606  			hostIP = "0.0.0.0"
   607  		}
   608  		apiCmd := slirp4netnsCmd{
   609  			Execute: "add_hostfwd",
   610  			Args: slirp4netnsCmdArg{
   611  				Proto:     i.Protocol,
   612  				HostAddr:  hostIP,
   613  				HostPort:  i.HostPort,
   614  				GuestPort: i.ContainerPort,
   615  			},
   616  		}
   617  		// create the JSON payload and send it.  Mark the end of request shutting down writes
   618  		// to the socket, as requested by slirp4netns.
   619  		data, err := json.Marshal(&apiCmd)
   620  		if err != nil {
   621  			return errors.Wrapf(err, "cannot marshal JSON for slirp4netns")
   622  		}
   623  		if _, err := conn.Write([]byte(fmt.Sprintf("%s\n", data))); err != nil {
   624  			return errors.Wrapf(err, "cannot write to control socket %s", apiSocket)
   625  		}
   626  		if err := conn.(*net.UnixConn).CloseWrite(); err != nil {
   627  			return errors.Wrapf(err, "cannot shutdown the socket %s", apiSocket)
   628  		}
   629  		buf := make([]byte, 2048)
   630  		readLength, err := conn.Read(buf)
   631  		if err != nil {
   632  			return errors.Wrapf(err, "cannot read from control socket %s", apiSocket)
   633  		}
   634  		// if there is no 'error' key in the received JSON data, then the operation was
   635  		// successful.
   636  		var y map[string]interface{}
   637  		if err := json.Unmarshal(buf[0:readLength], &y); err != nil {
   638  			return errors.Wrapf(err, "error parsing error status from slirp4netns")
   639  		}
   640  		if e, found := y["error"]; found {
   641  			return errors.Errorf("error from slirp4netns while setting up port redirection: %v", e)
   642  		}
   643  	}
   644  	logrus.Debug("slirp4netns port-forwarding setup via add_hostfwd is ready")
   645  	return nil
   646  }
   647  
   648  func getRootlessPortChildIP(c *Container) string {
   649  	if c.config.NetMode.IsSlirp4netns() {
   650  		slirp4netnsIP, err := GetSlirp4netnsIP(c.slirp4netnsSubnet)
   651  		if err != nil {
   652  			return ""
   653  		}
   654  		return slirp4netnsIP.String()
   655  	}
   656  
   657  	for _, r := range c.state.NetworkStatus {
   658  		for _, i := range r.IPs {
   659  			ipv4 := i.Address.IP.To4()
   660  			if ipv4 != nil {
   661  				return ipv4.String()
   662  			}
   663  		}
   664  	}
   665  	return ""
   666  }
   667  
   668  // reloadRootlessRLKPortMapping will trigger a reload for the port mappings in the rootlessport process.
   669  // This should only be called by network connect/disconnect and only as rootless.
   670  func (c *Container) reloadRootlessRLKPortMapping() error {
   671  	if len(c.config.PortMappings) == 0 {
   672  		return nil
   673  	}
   674  	childIP := getRootlessPortChildIP(c)
   675  	logrus.Debugf("reloading rootless ports for container %s, childIP is %s", c.config.ID, childIP)
   676  
   677  	conn, err := openUnixSocket(filepath.Join(c.runtime.config.Engine.TmpDir, "rp", c.config.ID))
   678  	if err != nil {
   679  		// This is not a hard error for backwards compatibility. A container started
   680  		// with an old version did not created the rootlessport socket.
   681  		logrus.Warnf("Could not reload rootless port mappings, port forwarding may no longer work correctly: %v", err)
   682  		return nil
   683  	}
   684  	defer conn.Close()
   685  	enc := json.NewEncoder(conn)
   686  	err = enc.Encode(childIP)
   687  	if err != nil {
   688  		return errors.Wrap(err, "port reloading failed")
   689  	}
   690  	b, err := ioutil.ReadAll(conn)
   691  	if err != nil {
   692  		return errors.Wrap(err, "port reloading failed")
   693  	}
   694  	data := string(b)
   695  	if data != "OK" {
   696  		return errors.Errorf("port reloading failed: %s", data)
   697  	}
   698  	return nil
   699  }