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 }