github.com/rootless-containers/rootlesskit/v2@v2.3.4/pkg/network/slirp4netns/slirp4netns.go (about) 1 package slirp4netns 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "io" 8 "net" 9 "os" 10 "os/exec" 11 "strconv" 12 "strings" 13 "sync" 14 "syscall" 15 "time" 16 17 "github.com/sirupsen/logrus" 18 "golang.org/x/sys/unix" 19 20 "github.com/rootless-containers/rootlesskit/v2/pkg/api" 21 "github.com/rootless-containers/rootlesskit/v2/pkg/common" 22 "github.com/rootless-containers/rootlesskit/v2/pkg/messages" 23 "github.com/rootless-containers/rootlesskit/v2/pkg/network" 24 "github.com/rootless-containers/rootlesskit/v2/pkg/network/iputils" 25 "github.com/rootless-containers/rootlesskit/v2/pkg/network/parentutils" 26 ) 27 28 type Features struct { 29 // SupportsEnableIPv6 --enable-ipv6 (v0.2.0) 30 SupportsEnableIPv6 bool 31 // SupportsCIDR --cidr (v0.3.0) 32 SupportsCIDR bool 33 // SupportsDisableHostLoopback --disable-host-loopback (v0.3.0) 34 SupportsDisableHostLoopback bool 35 // SupportsAPISocket --api-socket (v0.3.0) 36 SupportsAPISocket bool 37 // SupportsEnableSandbox --enable-sandbox (v0.4.0) 38 SupportsEnableSandbox bool 39 // SupportsEnableSeccomp --enable-seccomp (v0.4.0) 40 SupportsEnableSeccomp bool 41 // KernelSupportsSeccomp whether the kernel supports slirp4netns --enable-seccomp 42 KernelSupportsEnableSeccomp bool 43 } 44 45 func DetectFeatures(binary string) (*Features, error) { 46 if binary == "" { 47 return nil, errors.New("got empty slirp4netns binary") 48 } 49 realBinary, err := exec.LookPath(binary) 50 if err != nil { 51 return nil, fmt.Errorf("slirp4netns binary %q is not installed: %w", binary, err) 52 } 53 cmd := exec.Command(realBinary, "--help") 54 cmd.Env = os.Environ() 55 b, err := cmd.CombinedOutput() 56 s := string(b) 57 if err != nil { 58 return nil, fmt.Errorf( 59 "command \"%s --help\" failed, make sure slirp4netns v0.4.0+ is installed: %q: %w", 60 realBinary, s, err, 61 ) 62 } 63 if !strings.Contains(s, "--netns-type") { 64 // We don't use --netns-type, but we check the presence of --netns-type to 65 // ensure slirp4netns >= v0.4.0: https://github.com/rootless-containers/rootlesskit/issues/143 66 return nil, errors.New("slirp4netns seems older than v0.4.0") 67 } 68 kernelSupportsEnableSeccomp := false 69 if unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0) != unix.EINVAL { 70 kernelSupportsEnableSeccomp = unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0) != unix.EINVAL 71 } 72 f := Features{ 73 SupportsEnableIPv6: strings.Contains(s, "--enable-ipv6"), 74 SupportsCIDR: strings.Contains(s, "--cidr"), 75 SupportsDisableHostLoopback: strings.Contains(s, "--disable-host-loopback"), 76 SupportsAPISocket: strings.Contains(s, "--api-socket"), 77 SupportsEnableSandbox: strings.Contains(s, "--enable-sandbox"), 78 SupportsEnableSeccomp: strings.Contains(s, "--enable-seccomp"), 79 KernelSupportsEnableSeccomp: kernelSupportsEnableSeccomp, 80 } 81 return &f, nil 82 } 83 84 // NewParentDriver instantiates new parent driver. 85 // Requires slirp4netns v0.4.0 or later. 86 func NewParentDriver(logWriter io.Writer, binary string, mtu int, ipnet *net.IPNet, ifname string, disableHostLoopback bool, apiSocketPath string, 87 enableSandbox, enableSeccomp, enableIPv6 bool) (network.ParentDriver, error) { 88 if binary == "" { 89 return nil, errors.New("got empty slirp4netns binary") 90 } 91 if mtu < 0 { 92 return nil, errors.New("got negative mtu") 93 } 94 if mtu == 0 { 95 mtu = 65520 96 } 97 98 if ifname == "" { 99 ifname = "tap0" 100 } 101 102 features, err := DetectFeatures(binary) 103 if err != nil { 104 return nil, err 105 } 106 if enableIPv6 && !features.SupportsEnableIPv6 { 107 return nil, errors.New("this version of slirp4netns does not support --enable-ipv6") 108 } 109 if ipnet != nil && !features.SupportsCIDR { 110 return nil, errors.New("this version of slirp4netns does not support --cidr") 111 } 112 if disableHostLoopback && !features.SupportsDisableHostLoopback { 113 return nil, errors.New("this version of slirp4netns does not support --disable-host-loopback") 114 } 115 if apiSocketPath != "" && !features.SupportsAPISocket { 116 return nil, errors.New("this version of slirp4netns does not support --api-socket") 117 } 118 if enableSandbox && !features.SupportsEnableSandbox { 119 return nil, errors.New("this version of slirp4netns does not support --enable-sandbox") 120 } 121 if enableSeccomp && !features.SupportsEnableSeccomp { 122 return nil, errors.New("this version of slirp4netns does not support --enable-seccomp") 123 } 124 if enableSeccomp && !features.KernelSupportsEnableSeccomp { 125 return nil, errors.New("kernel does not support seccomp") 126 } 127 128 return &parentDriver{ 129 logWriter: logWriter, 130 binary: binary, 131 mtu: mtu, 132 ipnet: ipnet, 133 disableHostLoopback: disableHostLoopback, 134 apiSocketPath: apiSocketPath, 135 enableSandbox: enableSandbox, 136 enableSeccomp: enableSeccomp, 137 enableIPv6: enableIPv6, 138 ifname: ifname, 139 }, nil 140 } 141 142 type parentDriver struct { 143 logWriter io.Writer 144 binary string 145 mtu int 146 ipnet *net.IPNet 147 disableHostLoopback bool 148 apiSocketPath string 149 enableSandbox bool 150 enableSeccomp bool 151 enableIPv6 bool 152 ifname string 153 infoMu sync.RWMutex 154 info func() *api.NetworkDriverInfo 155 } 156 157 const DriverName = "slirp4netns" 158 159 func (d *parentDriver) Info(ctx context.Context) (*api.NetworkDriverInfo, error) { 160 d.infoMu.RLock() 161 infoFn := d.info 162 d.infoMu.RUnlock() 163 if infoFn == nil { 164 return &api.NetworkDriverInfo{ 165 Driver: DriverName, 166 }, nil 167 } 168 169 return infoFn(), nil 170 } 171 172 func (d *parentDriver) MTU() int { 173 return d.mtu 174 } 175 176 func (d *parentDriver) ConfigureNetwork(childPID int, stateDir, detachedNetNSPath string) (*messages.ParentInitNetworkDriverCompleted, func() error, error) { 177 if detachedNetNSPath != "" && d.enableSandbox { 178 return nil, nil, errors.New("slirp4netns sandbox is not compatible with detach-netns (https://github.com/rootless-containers/slirp4netns/issues/317)") 179 } 180 tap := d.ifname 181 var cleanups []func() error 182 if err := parentutils.PrepareTap(childPID, detachedNetNSPath, tap); err != nil { 183 return nil, common.Seq(cleanups), fmt.Errorf("setting up tap %s: %w", tap, err) 184 } 185 readyR, readyW, err := os.Pipe() 186 if err != nil { 187 return nil, common.Seq(cleanups), err 188 } 189 defer readyR.Close() 190 defer readyW.Close() 191 // -r: readyFD (requires slirp4netns >= v0.4.0: https://github.com/rootless-containers/rootlesskit/issues/143) 192 opts := []string{"--mtu", strconv.Itoa(d.mtu), "-r", "3"} 193 if d.disableHostLoopback { 194 opts = append(opts, "--disable-host-loopback") 195 } 196 if d.ipnet != nil { 197 opts = append(opts, "--cidr", d.ipnet.String()) 198 } 199 if d.apiSocketPath != "" { 200 opts = append(opts, "--api-socket", d.apiSocketPath) 201 } 202 if d.enableSandbox { 203 opts = append(opts, "--enable-sandbox") 204 } 205 if d.enableSeccomp { 206 opts = append(opts, "--enable-seccomp") 207 } 208 if d.enableIPv6 { 209 opts = append(opts, "--enable-ipv6") 210 } 211 if detachedNetNSPath == "" { 212 opts = append(opts, strconv.Itoa(childPID)) 213 } else { 214 opts = append(opts, 215 fmt.Sprintf("--userns-path=/proc/%d/ns/user", childPID), 216 "--netns-type=path", 217 detachedNetNSPath) 218 } 219 opts = append(opts, tap) 220 cmd := exec.Command(d.binary, opts...) 221 // FIXME: Stdout doen't seem captured 222 cmd.Stdout = d.logWriter 223 cmd.Stderr = d.logWriter 224 cmd.SysProcAttr = &syscall.SysProcAttr{ 225 Pdeathsig: syscall.SIGKILL, 226 } 227 cmd.ExtraFiles = append(cmd.ExtraFiles, readyW) 228 cleanups = append(cleanups, func() error { 229 logrus.Debugf("killing slirp4netns") 230 if cmd.Process != nil { 231 _ = cmd.Process.Kill() 232 } 233 wErr := cmd.Wait() 234 logrus.Debugf("killed slirp4netns: %v", wErr) 235 return nil 236 }) 237 if err := cmd.Start(); err != nil { 238 return nil, common.Seq(cleanups), fmt.Errorf("executing %v: %w", cmd, err) 239 } 240 241 if err := waitForReadyFD(cmd.Process.Pid, readyR); err != nil { 242 return nil, common.Seq(cleanups), fmt.Errorf("waiting for ready fd (%v): %w", cmd, err) 243 } 244 netmsg := messages.ParentInitNetworkDriverCompleted{ 245 Dev: tap, 246 DNS: make([]string, 0, 2), 247 MTU: d.mtu, 248 } 249 if d.ipnet != nil { 250 // TODO: get the actual configuration via slirp4netns API? 251 x, err := iputils.AddIPInt(d.ipnet.IP, 100) 252 if err != nil { 253 return nil, common.Seq(cleanups), err 254 } 255 netmsg.IP = x.String() 256 netmsg.Netmask, _ = d.ipnet.Mask.Size() 257 x, err = iputils.AddIPInt(d.ipnet.IP, 2) 258 if err != nil { 259 return nil, common.Seq(cleanups), err 260 } 261 netmsg.Gateway = x.String() 262 x, err = iputils.AddIPInt(d.ipnet.IP, 3) 263 if err != nil { 264 return nil, common.Seq(cleanups), err 265 } 266 netmsg.DNS = append(netmsg.DNS, x.String()) 267 } else { 268 netmsg.IP = "10.0.2.100" 269 netmsg.Netmask = 24 270 netmsg.Gateway = "10.0.2.2" 271 netmsg.DNS = append(netmsg.DNS, "10.0.2.3") 272 } 273 274 if d.enableIPv6 { 275 // for now slirp4netns only supports fd00::3 as v6 nameserver 276 // https://github.com/rootless-containers/slirp4netns/blob/ee1542e1532e6a7f266b8b6118973ab3b10a8bb5/slirp4netns.c#L272 277 netmsg.DNS = append(netmsg.DNS, "fd00::3") 278 } 279 280 apiDNS := make([]net.IP, 0, cap(netmsg.DNS)) 281 for _, nameserver := range netmsg.DNS { 282 apiDNS = append(apiDNS, net.ParseIP(nameserver)) 283 } 284 285 d.infoMu.Lock() 286 d.info = func() *api.NetworkDriverInfo { 287 return &api.NetworkDriverInfo{ 288 Driver: DriverName, 289 DNS: apiDNS, 290 ChildIP: net.ParseIP(netmsg.IP), 291 DynamicChildIP: false, 292 } 293 } 294 d.infoMu.Unlock() 295 return &netmsg, common.Seq(cleanups), nil 296 } 297 298 // waitForReady is from libpod 299 // https://github.com/containers/libpod/blob/e6b843312b93ddaf99d0ef94a7e60ff66bc0eac8/libpod/networking_linux.go#L272-L308 300 func waitForReadyFD(cmdPid int, r *os.File) error { 301 b := make([]byte, 16) 302 for { 303 if err := r.SetDeadline(time.Now().Add(1 * time.Second)); err != nil { 304 return fmt.Errorf("error setting slirp4netns pipe timeout: %w", err) 305 } 306 if _, err := r.Read(b); err == nil { 307 break 308 } else { 309 if os.IsTimeout(err) { 310 // Check if the process is still running. 311 var status syscall.WaitStatus 312 pid, err := syscall.Wait4(cmdPid, &status, syscall.WNOHANG, nil) 313 if err != nil { 314 return fmt.Errorf("failed to read slirp4netns process status: %w", err) 315 } 316 if pid != cmdPid { 317 continue 318 } 319 if status.Exited() { 320 return errors.New("slirp4netns failed") 321 } 322 if status.Signaled() { 323 return errors.New("slirp4netns killed by signal") 324 } 325 continue 326 } 327 return fmt.Errorf("failed to read from slirp4netns sync pipe: %w", err) 328 } 329 } 330 return nil 331 } 332 333 func NewChildDriver() network.ChildDriver { 334 return &childDriver{} 335 } 336 337 type childDriver struct { 338 } 339 340 func (d *childDriver) ChildDriverInfo() (*network.ChildDriverInfo, error) { 341 return &network.ChildDriverInfo{ 342 ConfiguresInterface: false, 343 }, nil 344 } 345 346 func (d *childDriver) ConfigureNetworkChild(netmsg *messages.ParentInitNetworkDriverCompleted, detachedNetNSPath string) (string, error) { 347 tap := netmsg.Dev 348 if tap == "" { 349 return "", errors.New("could not determine the preconfigured tap") 350 } 351 // tap is created and "up". 352 // IP stuff and MTU are not configured by the parent here, 353 // and they are up to the child. 354 return tap, nil 355 }