From bd8b631fc12e924568c34bb1aaa4313196e2ec85 Mon Sep 17 00:00:00 2001 From: fahed dorgaa Date: Wed, 30 Jul 2025 17:27:47 +0200 Subject: [PATCH 1/5] Add gvisortapvsock port driver support Signed-off-by: fahed dorgaa --- .github/workflows/main.yaml | 16 + cmd/rootlesskit/main.go | 16 +- docs/network.md | 2 + docs/port.md | 15 +- go.mod | 14 +- go.sum | 40 +-- hack/make-cross.sh | 3 +- pkg/messages/messages.go | 1 + pkg/network/gvisortapvsock/gvisortapvsock.go | 5 +- pkg/parent/parent.go | 7 + pkg/port/gvisortapvsock/gvisortapvsock.go | 343 +++++++++++++++++++ pkg/port/port.go | 13 + 12 files changed, 438 insertions(+), 37 deletions(-) create mode 100644 pkg/port/gvisortapvsock/gvisortapvsock.go diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 87c893b4..184be738 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -175,6 +175,14 @@ jobs: run: | docker run --rm --security-opt seccomp=unconfined --security-opt apparmor=unconfined --device /dev/net/tun \ rootlesskit:test-integration ./benchmark-iperf3-port.sh implicit --net=pasta --detach-netns + - name: "Benchmark: TCP Ports (network driver=gvisor-tap-vsock, port driver=gvisor-tap-vsock)" + run: | + docker run --rm --security-opt seccomp=unconfined --security-opt apparmor=unconfined --device /dev/net/tun \ + rootlesskit:test-integration ./benchmark-iperf3-port.sh gvisor-tap-vsock --net=gvisor-tap-vsock + - name: "Benchmark: TCP Ports (network driver=gvisor-tap-vsock, port driver=gvisor-tap-vsock) with detach-netns" + run: | + docker run --rm --security-opt seccomp=unconfined --security-opt apparmor=unconfined --device /dev/net/tun \ + rootlesskit:test-integration ./benchmark-iperf3-port.sh gvisor-tap-vsock --net=gvisor-tap-vsock --detach-netns # ===== Benchmark: UDP Ports ===== - name: "Benchmark: UDP Ports (port driver=slirp4netns)" run: | @@ -200,6 +208,14 @@ jobs: run: | docker run --rm --security-opt seccomp=unconfined --security-opt apparmor=unconfined --device /dev/net/tun \ rootlesskit:test-integration ./benchmark-iperf3-port-udp.sh builtin --detach-netns + - name: "Benchmark: UDP Ports (network driver=gvisor-tap-vsock, port driver=gvisor-tap-vsock)" + run: | + docker run --rm --security-opt seccomp=unconfined --security-opt apparmor=unconfined --device /dev/net/tun \ + rootlesskit:test-integration ./benchmark-iperf3-port-udp.sh gvisor-tap-vsock --net=gvisor-tap-vsock + - name: "Benchmark: UDP Ports (network driver=gvisor-tap-vsock, port driver=gvisor-tap-vsock) with detach-netns" + run: | + docker run --rm --security-opt seccomp=unconfined --security-opt apparmor=unconfined --device /dev/net/tun \ + rootlesskit:test-integration ./benchmark-iperf3-port-udp.sh gvisor-tap-vsock --net=gvisor-tap-vsock --detach-netns test-integration-docker: name: "Integration test (Docker)" diff --git a/cmd/rootlesskit/main.go b/cmd/rootlesskit/main.go index 53e32f99..52f46de0 100644 --- a/cmd/rootlesskit/main.go +++ b/cmd/rootlesskit/main.go @@ -27,6 +27,7 @@ import ( "github.com/rootless-containers/rootlesskit/v2/pkg/network/vpnkit" "github.com/rootless-containers/rootlesskit/v2/pkg/parent" "github.com/rootless-containers/rootlesskit/v2/pkg/port/builtin" + gvisortapvsock_port "github.com/rootless-containers/rootlesskit/v2/pkg/port/gvisortapvsock" "github.com/rootless-containers/rootlesskit/v2/pkg/port/portutil" slirp4netns_port "github.com/rootless-containers/rootlesskit/v2/pkg/port/slirp4netns" "github.com/rootless-containers/rootlesskit/v2/pkg/systemd/activation" @@ -168,7 +169,7 @@ See https://rootlesscontaine.rs/getting-started/common/ . }, CategoryMount), Categorize(&cli.StringFlag{ Name: "port-driver", - Usage: "port driver for non-host network. [none, implicit (for pasta), builtin, slirp4netns]", + Usage: "port driver for non-host network. [none, implicit (for pasta), builtin, slirp4netns, gvisor-tap-vsock]", Value: "none", }, CategoryPort), Categorize(&cli.StringSliceFlag{ @@ -558,7 +559,7 @@ func createParentOpt(clicontext *cli.Context) (parent.Opt, error) { if clicontext.Bool("ipv6") { // virtual network does not support IPv6 yet - // see https://github.com/containers/gvisor-tap-vsock/blob/v0.8.6/pkg/virtualnetwork/virtualnetwork.go#L102 + // see https://github.com/containers/gvisor-tap-vsock/blob/v0.8.7/pkg/virtualnetwork/virtualnetwork.go#L102 return opt, errors.New("--ipv6 is not supported for gvisor-tap-vsock") } @@ -596,6 +597,15 @@ func createParentOpt(clicontext *cli.Context) (parent.Opt, error) { if err != nil { return opt, err } + case "gvisor-tap-vsock": + logrus.Warn("\"gvisor-tap-vsock\" port driver is experimental") + if opt.NetworkDriver == nil { + return opt, errors.New("port driver requires non-host network") + } + opt.PortDriver, err = gvisortapvsock_port.NewParentDriver(&logrusDebugWriter{label: "port/gvisor-tap-vsock"}, opt.StateDir) + if err != nil { + return opt, err + } default: return opt, fmt.Errorf("unknown port driver: %s", s) } @@ -687,6 +697,8 @@ func createChildOpt(clicontext *cli.Context) (child.Opt, error) { opt.PortDriver = slirp4netns_port.NewChildDriver() case "builtin": opt.PortDriver = builtin.NewChildDriver(&logrusDebugWriter{label: "port/builtin"}) + case "gvisor-tap-vsock": + opt.PortDriver = gvisortapvsock_port.NewChildDriver() default: return opt, fmt.Errorf("unknown port driver: %s", s) } diff --git a/docs/network.md b/docs/network.md index cc647f9e..ab8c9d7a 100644 --- a/docs/network.md +++ b/docs/network.md @@ -212,6 +212,8 @@ Currently, the MAC address is always set to a random address. `--net=gvisor-tap-vsock` isolates the network namespace from the host and uses [gvisor-tap-vsock](https://github.com/containers/gvisor-tap-vsock) for providing usermode networking. +> Note: When used together with the `gvisor-tap-vsock` port driver, port-forward throughput is currently slower than other drivers. The port driver is experimental. Ideas for improving throughput are being tracked here: https://github.com/rootless-containers/rootlesskit/issues/529 + Pros: * Possible to perform network-namespaced operations, e.g. creating iptables rules, running `tcpdump` * Supports ICMP Echo (`ping`) when `/proc/sys/net/ipv4/ping_group_range` is configured diff --git a/docs/port.md b/docs/port.md index e6a00278..0fa3c570 100644 --- a/docs/port.md +++ b/docs/port.md @@ -4,11 +4,12 @@ To the ports in the network namespace to the host network namespace, `--port-dri The default value is `none` (do not expose ports). -| `--port-driver` | Throughput | Source IP -|----------------------|-------------|---------- -| `slirp4netns` | 6.89 Gbps | Propagated -| `socat` (Deprecated) | 7.80 Gbps | Always 127.0.0.1 -| `builtin` | 30.0 Gbps | Always 127.0.0.1 +| `--port-driver` | Throughput | Source IP | Notes +|----------------------|-------------|----------|------- +| `slirp4netns` | 2.51 Gbps | Propagated | +| `socat` (Deprecated) | 7.80 Gbps | Always 127.0.0.1 | +| `builtin` | 32.6 Gbps | Always 127.0.0.1 | +| `gvisor-tap-vsock` (Experimental) | 2.26 Gbps | Propagated | Throughput is currently limited; see issue link below for improvement ideas. ([Benchmark: iperf3 from the parent to the child (Mar 8, 2020)](https://github.com/rootless-containers/rootlesskit/runs/492498728)) @@ -16,6 +17,10 @@ The `builtin` driver is fast, but be aware that the source IP is not propagated For [`pasta`](./network.md) networks, the `implicit` port driver is the best choice. +For [`gVisor TAP/vsock`](https://github.com/containers/gvisor-tap-vsock) based networks, use the `gvisor-tap-vsock` port driver. + +> Note: The `gvisor-tap-vsock` port driver is experimental. Current throughput is known to be slower than other drivers. We are tracking ideas for improving throughput here: https://github.com/rootless-containers/rootlesskit/issues/529 + * To be documented: [`bypass4netns`](https://github.com/rootless-containers/bypass4netns) for native performance. ### Exposing ports diff --git a/go.mod b/go.mod index 57b307d3..83d27325 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.23.0 require ( github.com/Masterminds/semver/v3 v3.4.0 github.com/containernetworking/plugins v1.7.1 - github.com/containers/gvisor-tap-vsock v0.8.6 + github.com/containers/gvisor-tap-vsock v0.8.7 github.com/gofrs/flock v0.12.1 github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 @@ -15,6 +15,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 github.com/urfave/cli/v2 v2.27.7 + golang.org/x/sync v0.16.0 golang.org/x/sys v0.35.0 gotest.tools/v3 v3.5.2 ) @@ -26,17 +27,16 @@ require ( github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/miekg/dns v1.1.65 // indirect + github.com/miekg/dns v1.1.68 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect - golang.org/x/crypto v0.38.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.38.0 // indirect - golang.org/x/sync v0.14.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/net v0.42.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.31.0 // indirect + golang.org/x/tools v0.35.0 // indirect gvisor.dev/gvisor v0.0.0-20240916094835-a174eb65023f // indirect ) diff --git a/go.sum b/go.sum index 1d015e90..e243b0f2 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEm github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4= github.com/containernetworking/plugins v1.7.1 h1:CNAR0jviDj6FS5Vg85NTgKWLDzZPfi/lj+VJfhMDTIs= github.com/containernetworking/plugins v1.7.1/go.mod h1:xuMdjuio+a1oVQsHKjr/mgzuZ24leAsqUYRnzGoXHy0= -github.com/containers/gvisor-tap-vsock v0.8.6 h1:9SeAXK+K2o36CtrgYk6zRXbU3zrayjvkrI8b7/O6u5A= -github.com/containers/gvisor-tap-vsock v0.8.6/go.mod h1:+0mtKmm4STeSDnZe+DGnIwN4EH2f7AcWir7PwT28Ti0= +github.com/containers/gvisor-tap-vsock v0.8.7 h1:mFMMU5CIXO9sbtsgECc90loUHx15km3AN6Zuhg3X4qM= +github.com/containers/gvisor-tap-vsock v0.8.7/go.mod h1:Rf2gm4Lpac0IZbg8wwQDh7UuKCxHmnxar0hEZ08OXY8= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -43,8 +43,8 @@ github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= -github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc= -github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck= +github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= +github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= github.com/moby/vpnkit v0.6.0 h1:HEh3iQ57oigvPNbR89R14pw3difgPyFOMMD3JAoqPoY= @@ -55,8 +55,8 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= -github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= -github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY= +github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -85,34 +85,34 @@ go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= -golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= -golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= diff --git a/hack/make-cross.sh b/hack/make-cross.sh index 4f5dcbde..b707a0a4 100755 --- a/hack/make-cross.sh +++ b/hack/make-cross.sh @@ -24,6 +24,7 @@ x ppc64le ppc64le x riscv64 riscv64 GOARM=7 export GOARM -x arm armv7l +# Skip building for arm due to gvisor-tap-vsock(v0.8.7) incompatibility on 32-bit ARM (see https://github.com/containers/gvisor-tap-vsock/issues/545)) +#x arm armv7l rm -rf bin diff --git a/pkg/messages/messages.go b/pkg/messages/messages.go index 83466be6..f365bb30 100644 --- a/pkg/messages/messages.go +++ b/pkg/messages/messages.go @@ -55,6 +55,7 @@ type ChildInitUserNSCompleted struct { type ParentInitNetworkDriverCompleted struct { // Fields are empty for HostNetwork. + Network interface{} Dev string IP string Netmask int diff --git a/pkg/network/gvisortapvsock/gvisortapvsock.go b/pkg/network/gvisortapvsock/gvisortapvsock.go index 89e03405..17caeaca 100644 --- a/pkg/network/gvisortapvsock/gvisortapvsock.go +++ b/pkg/network/gvisortapvsock/gvisortapvsock.go @@ -164,13 +164,14 @@ func (d *parentDriver) setupDNSServers() ([]string, error) { } // prepareNetworkMessage creates the network message with all configuration details -func (d *parentDriver) prepareNetworkMessage(tap string, ip string, netmask int, gateway string) (*messages.ParentInitNetworkDriverCompleted, error) { +func (d *parentDriver) prepareNetworkMessage(virtualNetwork *virtualnetwork.VirtualNetwork, tap string, ip string, netmask int, gateway string) (*messages.ParentInitNetworkDriverCompleted, error) { dnsServers, err := d.setupDNSServers() if err != nil { return nil, err } netmsg := messages.ParentInitNetworkDriverCompleted{ + Network: virtualNetwork, Dev: tap, DNS: dnsServers, MTU: d.mtu, @@ -346,7 +347,7 @@ func (d *parentDriver) ConfigureNetwork(childPID int, stateDir, detachedNetNSPat cleanups = append(cleanups, d.createCleanupFunc(vn)) // Prepare network message - netmsg, err := d.prepareNetworkMessage(tap, ip, netmask, gateway) + netmsg, err := d.prepareNetworkMessage(vn, tap, ip, netmask, gateway) if err != nil { return nil, common.Seq(cleanups), err } diff --git a/pkg/parent/parent.go b/pkg/parent/parent.go index 733d603e..cd9288a2 100644 --- a/pkg/parent/parent.go +++ b/pkg/parent/parent.go @@ -290,6 +290,13 @@ func Parent(opt Opt) error { msgParentInitPortDriverCompleted.U.ParentInitPortDriverCompleted.PortDriverOpaque = opt.PortDriver.OpaqueForChild() cctx := &port.ChildContext{ IP: net.ParseIP(msgParentInitNetworkDriverCompleted.U.ParentInitNetworkDriverCompleted.IP).To4(), + Network: func() port.VirtualNetworkProvider { + if v, ok := msgParentInitNetworkDriverCompleted.U.ParentInitNetworkDriverCompleted.Network.(port.VirtualNetworkProvider); ok { + return v + } + return nil + }(), + GatewayIP: net.ParseIP(msgParentInitNetworkDriverCompleted.U.ParentInitNetworkDriverCompleted.Gateway).To4(), } go func() { portDriverErr <- opt.PortDriver.RunParentDriver(portDriverInitComplete, diff --git a/pkg/port/gvisortapvsock/gvisortapvsock.go b/pkg/port/gvisortapvsock/gvisortapvsock.go new file mode 100644 index 00000000..7164d231 --- /dev/null +++ b/pkg/port/gvisortapvsock/gvisortapvsock.go @@ -0,0 +1,343 @@ +package gvisortapvsock + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net" + "net/http" + "net/http/httptest" + "strings" + "sync" + "time" + + "golang.org/x/sync/errgroup" + + "github.com/containers/gvisor-tap-vsock/pkg/types" + "github.com/rootless-containers/rootlesskit/v2/pkg/api" + "github.com/rootless-containers/rootlesskit/v2/pkg/port" + "github.com/sirupsen/logrus" +) + +// NewParentDriver creates a new parent driver using gvisor-tap-vsock for port forwarding. +func NewParentDriver(logWriter io.Writer, stateDir string) (port.ParentDriver, error) { + d := &driver{ + logWriter: logWriter, + stateDir: stateDir, + ports: make(map[int]*port.Status), + portSeq: 1, + ctx: context.Background(), + } + return d, nil +} + +// exposePort uses the HTTP API to expose a port +func (d *driver) exposePort(protocol types.TransportProtocol, local, remote string) error { + d.mu.Lock() + defer d.mu.Unlock() + + if d.servicesMux == nil { + return errors.New("services mux not initialized") + } + + // Create a request to the PortsForwarder's HTTP API + req := types.ExposeRequest{ + Protocol: protocol, + Local: local, + Remote: remote, + } + + // Create a buffer to store the request body + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(req); err != nil { + return fmt.Errorf("failed to encode request: %w", err) + } + + u := fmt.Sprintf("http://%s/services/forwarder/expose", d.gatewayIP.String()) + httpReq, err := http.NewRequest(http.MethodPost, u, &buf) + if err != nil { + return fmt.Errorf("failed to create request: %w", err) + } + httpReq.Header.Set("Content-Type", "application/json") + + // Create a response recorder + rr := httptest.NewRecorder() + + // Serve the request + d.servicesMux.ServeHTTP(rr, httpReq) + + // Check the response + if rr.Code != http.StatusOK { + return fmt.Errorf("expose request failed with status %d: %s", rr.Code, rr.Body.String()) + } + + return nil +} + +// unexposePort uses the HTTP API to unexpose a port +func (d *driver) unexposePort(protocol types.TransportProtocol, local string) error { + d.mu.Lock() + defer d.mu.Unlock() + + if d.servicesMux == nil { + return errors.New("services mux not initialized") + } + + // Create a request to the PortsForwarder's HTTP API + req := types.UnexposeRequest{ + Protocol: protocol, + Local: local, + } + + // Create a buffer to store the request body + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(req); err != nil { + return fmt.Errorf("failed to encode request: %w", err) + } + + u := fmt.Sprintf("http://%s/services/forwarder/unexpose", d.gatewayIP.String()) + httpReq, err := http.NewRequest(http.MethodPost, u, &buf) + if err != nil { + return fmt.Errorf("failed to create request: %w", err) + } + httpReq.Header.Set("Content-Type", "application/json") + + // Create a response recorder + rr := httptest.NewRecorder() + + // Serve the request + d.servicesMux.ServeHTTP(rr, httpReq) + + // Check the response + if rr.Code != http.StatusOK { + return fmt.Errorf("unexpose request failed with status %d: %s", rr.Code, rr.Body.String()) + } + + return nil +} + +func httpServe(ctx context.Context, g *errgroup.Group, ln net.Listener, mux http.Handler) { + g.Go(func() error { + <-ctx.Done() + return ln.Close() + }) + g.Go(func() error { + s := &http.Server{ + Handler: mux, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + } + err := s.Serve(ln) + if err != nil { + if err != http.ErrServerClosed { + return err + } + return err + } + return nil + }) +} + +// NewChildDriver creates a new child driver for gvisor-tap-vsock port forwarding. +func NewChildDriver() port.ChildDriver { + return &childDriver{} +} + +type driver struct { + logWriter io.Writer + stateDir string + mu sync.Mutex + ports map[int]*port.Status + portSeq int + ctx context.Context + cancel context.CancelFunc + childIP string + gatewayIP net.IP + servicesMux http.Handler +} + +func (d *driver) Info(_ context.Context) (*api.PortDriverInfo, error) { + return &api.PortDriverInfo{ + Driver: "gvisor-tap-vsock", + // No additional options needed for this driver + // as it uses the existing gvisor-tap-vsock network + }, nil +} + +func (d *driver) OpaqueForChild() map[string]string { + return map[string]string{} +} + +func (d *driver) RunParentDriver(initComplete chan struct{}, quit <-chan struct{}, cctx *port.ChildContext) error { + if cctx == nil || cctx.IP == nil { + return errors.New("child context IP is required") + } + + d.childIP = cctx.IP.String() + d.gatewayIP = cctx.GatewayIP + + d.mu.Lock() + + // Clean up existing virtual network if any + if d.cancel != nil { + d.cancel() + d.cancel = nil + } + + // Create a new context with cancellation + d.ctx, d.cancel = context.WithCancel(context.Background()) + + // Get the virtual network from the child context + if cctx != nil && cctx.Network != nil { + d.servicesMux = cctx.Network.Mux() + logrus.Debugf("Using services mux from child context") + } + + if d.servicesMux == nil { + return fmt.Errorf("Virtual network services mux not available in child context") + } + + d.mu.Unlock() + logrus.Debugf("Created virtual network") + + // Signal that initialization is complete + close(initComplete) + + // Wait for quit signal + <-quit + + // Cleanup + d.mu.Lock() + defer d.mu.Unlock() + + if d.cancel != nil { + d.cancel() + d.cancel = nil + } + + return nil +} + +func (d *driver) AddPort(ctx context.Context, spec port.Spec) (*port.Status, error) { + d.mu.Lock() + + // Validate protocol + if !strings.HasPrefix(spec.Proto, "tcp") && !strings.HasPrefix(spec.Proto, "udp") { + d.mu.Unlock() + return nil, fmt.Errorf("unsupported protocol: %s", spec.Proto) + } + + // Set gvisor-tap-vsock's child IP if not specified + if spec.ChildIP == "" { + spec.ChildIP = d.childIP + } + + // Create port status + id := d.portSeq + d.portSeq++ + st := &port.Status{ + ID: id, + Spec: spec, + } + d.ports[id] = st + + // Format the local and remote addresses + localAddr := fmt.Sprintf("%s:%d", spec.ParentIP, spec.ParentPort) + remoteAddr := fmt.Sprintf("%s:%d", spec.ChildIP, spec.ChildPort) + + // Determine the protocol + var protocol types.TransportProtocol = types.TCP + if strings.HasPrefix(spec.Proto, "udp") { + protocol = types.UDP + // Add UDP prefix to the key for tracking in forwardsMap + localAddr = "udp:" + localAddr + } + + // Unlock before calling exposePort to avoid deadlock + d.mu.Unlock() + + // For TCP, use the localAddr directly; for UDP, remove the "udp:" prefix + exposeLocalAddr := localAddr + if protocol == types.UDP { + exposeLocalAddr = strings.TrimPrefix(localAddr, "udp:") + } + + // Call exposePort to directly use the PortsForwarder without recreating the network + err := d.exposePort(protocol, exposeLocalAddr, remoteAddr) + + if err != nil { + // Need to lock again for cleanup on error + d.mu.Lock() + delete(d.ports, id) + d.mu.Unlock() + return nil, fmt.Errorf("failed to expose port: %w", err) + } + + return st, nil +} + +func (d *driver) ListPorts(ctx context.Context) ([]port.Status, error) { + d.mu.Lock() + defer d.mu.Unlock() + var ports []port.Status + for _, p := range d.ports { + ports = append(ports, *p) + } + return ports, nil +} + +func (d *driver) RemovePort(ctx context.Context, id int) error { + d.mu.Lock() + + st, ok := d.ports[id] + if !ok { + d.mu.Unlock() + return fmt.Errorf("unknown port id: %d", id) + } + + // Format the local address + proto := st.Spec.Proto + localAddr := fmt.Sprintf("%s:%d", st.Spec.ParentIP, st.Spec.ParentPort) + + // Determine the protocol + var protocol types.TransportProtocol = types.TCP + if strings.HasPrefix(proto, "udp") { + protocol = types.UDP + // Add UDP prefix to the key for tracking in forwardsMap + localAddr = "udp:" + localAddr + } + + // Remove from ports map + delete(d.ports, id) + + // Unlock before calling unexposePort to avoid deadlock + d.mu.Unlock() + + // For TCP, use the localAddr directly; for UDP, remove the "udp:" prefix + unexposeLocalAddr := localAddr + if protocol == types.UDP { + unexposeLocalAddr = strings.TrimPrefix(localAddr, "udp:") + } + + err := d.unexposePort(protocol, unexposeLocalAddr) + + if err != nil { + logrus.Warnf("Failed to unexpose port %s: %v", localAddr, err) + // We don't return the error here because the port is already removed from our tracking + // and we don't want to block the removal operation due to API errors + } + + return nil +} + +type childDriver struct{} + +func (d *childDriver) RunChildDriver(opaque map[string]string, quit <-chan struct{}, detachedNetNSPath string) error { + // The child driver doesn't need to do anything special + // as the gvisor-tap-vsock network driver handles the forwarding + <-quit + return nil +} diff --git a/pkg/port/port.go b/pkg/port/port.go index d0213aed..76a115dc 100644 --- a/pkg/port/port.go +++ b/pkg/port/port.go @@ -3,6 +3,7 @@ package port import ( "context" "net" + "net/http" "github.com/rootless-containers/rootlesskit/v2/pkg/api" ) @@ -18,6 +19,7 @@ type Spec struct { // Default values: // - builtin driver: 127.0.0.1 // - slirp4netns driver: slirp4netns's child IP, e.g., 10.0.2.100 + // - gvisor-tap-vsock driver: gvisor-tap-vsock's child IP, e.g., 10.0.2.100 ChildIP string `json:"childIP,omitempty"` } @@ -33,10 +35,21 @@ type Manager interface { RemovePort(ctx context.Context, id int) error } +// VirtualNetworkProvider is a minimal interface exposed by network drivers +// that provide an in-process virtual network implementation usable by port drivers. +// For drivers that do not expose such a network object, this can be nil. +type VirtualNetworkProvider interface { + Mux() *http.ServeMux +} + // ChildContext is used for RunParentDriver type ChildContext struct { // IP of the tap device IP net.IP + // Network is the virtual network object from the network driver + Network VirtualNetworkProvider + // GatewayIP is the gateway IP address of the virtual network (e.g., 10.0.2.2) + GatewayIP net.IP } // ParentDriver is a driver for the parent process. From 185f2b19471f6d109928b14a27d703e9e0201d9f Mon Sep 17 00:00:00 2001 From: fahed dorgaa Date: Thu, 18 Sep 2025 15:51:49 +0200 Subject: [PATCH 2/5] bump go to version 1.24 (version 1.23 is not supported) update go.mod and go.sum for dependency upgrades and fix gvisor-tap-vsock compatibility Signed-off-by: fahed dorgaa --- go.mod | 17 ++++++++++------- go.sum | 40 ++++++++++++++++++++-------------------- hack/make-cross.sh | 3 +-- 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index 83d27325..6e72ec21 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/rootless-containers/rootlesskit/v2 -go 1.23.0 +go 1.24.0 require ( github.com/Masterminds/semver/v3 v3.4.0 @@ -15,8 +15,8 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 github.com/urfave/cli/v2 v2.27.7 - golang.org/x/sync v0.16.0 - golang.org/x/sys v0.35.0 + golang.org/x/sync v0.17.0 + golang.org/x/sys v0.36.0 gotest.tools/v3 v3.5.2 ) @@ -33,10 +33,13 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect - golang.org/x/crypto v0.41.0 // indirect - golang.org/x/mod v0.27.0 // indirect - golang.org/x/net v0.42.0 // indirect + golang.org/x/crypto v0.42.0 // indirect + golang.org/x/mod v0.28.0 // indirect + golang.org/x/net v0.43.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.35.0 // indirect + golang.org/x/tools v0.36.0 // indirect gvisor.dev/gvisor v0.0.0-20240916094835-a174eb65023f // indirect ) + +// Point to upstream main until the next release to get this fix: https://github.com/containers/gvisor-tap-vsock/commit/c4615cdb1462cb4c9ea5ab770ea11ad03c472c26 +replace github.com/containers/gvisor-tap-vsock => github.com/containers/gvisor-tap-vsock v0.8.8-0.20250909141233-c4615cdb1462 diff --git a/go.sum b/go.sum index e243b0f2..b1d5eba0 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEm github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4= github.com/containernetworking/plugins v1.7.1 h1:CNAR0jviDj6FS5Vg85NTgKWLDzZPfi/lj+VJfhMDTIs= github.com/containernetworking/plugins v1.7.1/go.mod h1:xuMdjuio+a1oVQsHKjr/mgzuZ24leAsqUYRnzGoXHy0= -github.com/containers/gvisor-tap-vsock v0.8.7 h1:mFMMU5CIXO9sbtsgECc90loUHx15km3AN6Zuhg3X4qM= -github.com/containers/gvisor-tap-vsock v0.8.7/go.mod h1:Rf2gm4Lpac0IZbg8wwQDh7UuKCxHmnxar0hEZ08OXY8= +github.com/containers/gvisor-tap-vsock v0.8.8-0.20250909141233-c4615cdb1462 h1:3epe2ZHb43TaYQmSIWPFfDawh7hdW5qgYPvu0xw3A8k= +github.com/containers/gvisor-tap-vsock v0.8.8-0.20250909141233-c4615cdb1462/go.mod h1:T1aCvysIQbAz+/gaJ31pe0DmROafTYBTpXsB1Toh8WI= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -71,8 +71,8 @@ github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM= github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA= github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= @@ -85,34 +85,34 @@ go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= diff --git a/hack/make-cross.sh b/hack/make-cross.sh index b707a0a4..4f5dcbde 100755 --- a/hack/make-cross.sh +++ b/hack/make-cross.sh @@ -24,7 +24,6 @@ x ppc64le ppc64le x riscv64 riscv64 GOARM=7 export GOARM -# Skip building for arm due to gvisor-tap-vsock(v0.8.7) incompatibility on 32-bit ARM (see https://github.com/containers/gvisor-tap-vsock/issues/545)) -#x arm armv7l +x arm armv7l rm -rf bin From 3c149348f89d3141ffee1586235baaf92aa573f9 Mon Sep 17 00:00:00 2001 From: fahed dorgaa Date: Thu, 18 Sep 2025 16:15:10 +0200 Subject: [PATCH 3/5] refactor: replace Debugf with Debug for logging messages Signed-off-by: fahed dorgaa --- pkg/network/gvisortapvsock/gvisortapvsock.go | 10 +++++----- pkg/network/slirp4netns/slirp4netns.go | 2 +- pkg/network/vpnkit/vpnkit.go | 4 ++-- pkg/parent/parent.go | 4 ++-- pkg/port/gvisortapvsock/gvisortapvsock.go | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/network/gvisortapvsock/gvisortapvsock.go b/pkg/network/gvisortapvsock/gvisortapvsock.go index 17caeaca..ce565f6a 100644 --- a/pkg/network/gvisortapvsock/gvisortapvsock.go +++ b/pkg/network/gvisortapvsock/gvisortapvsock.go @@ -243,7 +243,7 @@ func (d *parentDriver) acceptConnections() { if err != nil { // Check if the error is due to the listener being closed, which is expected during cleanup if errors.Is(err, io.EOF) || strings.Contains(err.Error(), "use of closed network connection") { - logrus.Debugf("listener closed, stopping accept loop") + logrus.Debug("listener closed, stopping accept loop") return } logrus.Errorf("accepting connection: %v", err) @@ -271,9 +271,9 @@ func (d *parentDriver) handleConnection(conn net.Conn) { // Use the AcceptStdio function to stream packets between the Unix socket and the virtual network // This will handle the connection and forward packets in both directions - logrus.Debugf("forwarding packets between Unix socket and virtual network using VfkitProtocol") + logrus.Debug("forwarding packets between Unix socket and virtual network using VfkitProtocol") if err := vn.AcceptStdio(d.ctx, conn); err != nil { - logrus.Debugf(err.Error()) + logrus.Debug(err) if errors.Is(err, io.EOF) { // This is expected when the child process exits logrus.Debugf("child process exited, connection closed: %v", err) @@ -286,7 +286,7 @@ func (d *parentDriver) handleConnection(conn net.Conn) { // createCleanupFunc creates a cleanup function for the virtual network func (d *parentDriver) createCleanupFunc(vn *virtualnetwork.VirtualNetwork) func() error { return func() error { - logrus.Debugf("closing gvisor-tap-vsock virtual network") + logrus.Debug("closing gvisor-tap-vsock virtual network") // The VirtualNetwork struct doesn't have an explicit Close method, // but we'll keep a reference to it to prevent garbage collection _ = vn @@ -301,7 +301,7 @@ func (d *parentDriver) createCleanupFunc(vn *virtualnetwork.VirtualNetwork) func } } - logrus.Debugf("closed gvisor-tap-vsock virtual network") + logrus.Debug("closed gvisor-tap-vsock virtual network") return nil } } diff --git a/pkg/network/slirp4netns/slirp4netns.go b/pkg/network/slirp4netns/slirp4netns.go index c4cdd3b4..a0e9cc10 100644 --- a/pkg/network/slirp4netns/slirp4netns.go +++ b/pkg/network/slirp4netns/slirp4netns.go @@ -226,7 +226,7 @@ func (d *parentDriver) ConfigureNetwork(childPID int, stateDir, detachedNetNSPat } cmd.ExtraFiles = append(cmd.ExtraFiles, readyW) cleanups = append(cleanups, func() error { - logrus.Debugf("killing slirp4netns") + logrus.Debug("killing slirp4netns") if cmd.Process != nil { _ = cmd.Process.Kill() } diff --git a/pkg/network/vpnkit/vpnkit.go b/pkg/network/vpnkit/vpnkit.go index 6a2b3a64..35318063 100644 --- a/pkg/network/vpnkit/vpnkit.go +++ b/pkg/network/vpnkit/vpnkit.go @@ -97,7 +97,7 @@ func (d *parentDriver) ConfigureNetwork(childPID int, stateDir, detachedNetNSPat Pdeathsig: syscall.SIGKILL, } cleanups = append(cleanups, func() error { - logrus.Debugf("killing vpnkit") + logrus.Debug("killing vpnkit") vpnkitCancel() wErr := vpnkitCmd.Wait() logrus.Debugf("killed vpnkit: %v", wErr) @@ -120,7 +120,7 @@ func (d *parentDriver) ConfigureNetwork(childPID int, stateDir, detachedNetNSPat if err != nil { return nil, common.Seq(cleanups), fmt.Errorf("connecting to %s with uuid %s: %w", vpnkitSocket, vifUUID, err) } - logrus.Debugf("connected to VPNKit vmnet") + logrus.Debug("connected to VPNKit vmnet") // TODO: support configuration netmsg := messages.ParentInitNetworkDriverCompleted{ Dev: d.ifname, diff --git a/pkg/parent/parent.go b/pkg/parent/parent.go index cd9288a2..cbed45ce 100644 --- a/pkg/parent/parent.go +++ b/pkg/parent/parent.go @@ -367,10 +367,10 @@ func getSubIDRanges(u *user.User, subidSource SubidSource) ([]idtools.SubIDRange } switch subidSource { case SubidSourceStatic: - logrus.Debugf("subid-source: using the static source") + logrus.Debug("subid-source: using the static source") return idtools.GetSubIDRanges(uid, u.Username) case SubidSourceDynamic: - logrus.Debugf("subid-source: using the dynamic source") + logrus.Debug("subid-source: using the dynamic source") return dynidtools.GetSubIDRanges(uid, u.Username) case "", SubidSourceAuto: subuidRanges, subgidRanges, err := getSubIDRanges(u, SubidSourceDynamic) diff --git a/pkg/port/gvisortapvsock/gvisortapvsock.go b/pkg/port/gvisortapvsock/gvisortapvsock.go index 7164d231..2b2effae 100644 --- a/pkg/port/gvisortapvsock/gvisortapvsock.go +++ b/pkg/port/gvisortapvsock/gvisortapvsock.go @@ -193,7 +193,7 @@ func (d *driver) RunParentDriver(initComplete chan struct{}, quit <-chan struct{ // Get the virtual network from the child context if cctx != nil && cctx.Network != nil { d.servicesMux = cctx.Network.Mux() - logrus.Debugf("Using services mux from child context") + logrus.Debug("Using services mux from child context") } if d.servicesMux == nil { @@ -201,7 +201,7 @@ func (d *driver) RunParentDriver(initComplete chan struct{}, quit <-chan struct{ } d.mu.Unlock() - logrus.Debugf("Created virtual network") + logrus.Debug("Created virtual network") // Signal that initialization is complete close(initComplete) From d0d6c23a2e61a199573000613b874ec129067539 Mon Sep 17 00:00:00 2001 From: fahed dorgaa Date: Fri, 19 Sep 2025 14:31:51 +0200 Subject: [PATCH 4/5] go.mod: update gvisor-tap-vsock to v0.8.8 and improve port driver benchmarks Signed-off-by: fahed dorgaa --- docs/port.md | 12 +++--------- go.mod | 5 +---- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/docs/port.md b/docs/port.md index 0fa3c570..6d0d2962 100644 --- a/docs/port.md +++ b/docs/port.md @@ -6,10 +6,9 @@ The default value is `none` (do not expose ports). | `--port-driver` | Throughput | Source IP | Notes |----------------------|-------------|----------|------- -| `slirp4netns` | 2.51 Gbps | Propagated | -| `socat` (Deprecated) | 7.80 Gbps | Always 127.0.0.1 | -| `builtin` | 32.6 Gbps | Always 127.0.0.1 | -| `gvisor-tap-vsock` (Experimental) | 2.26 Gbps | Propagated | Throughput is currently limited; see issue link below for improvement ideas. +| `slirp4netns` | 9.78 Gbps | Propagated | +| `builtin` | 35.6 Gbps | Always 127.0.0.1 | +| `gvisor-tap-vsock` (Experimental) | 3.99 Gbps | Propagated | Throughput is currently limited; see issue link below for improvement ideas. ([Benchmark: iperf3 from the parent to the child (Mar 8, 2020)](https://github.com/rootless-containers/rootlesskit/runs/492498728)) @@ -37,11 +36,6 @@ rootlesskit$ rootlessctl --socket=/run/user/1001/rootlesskit/foo/api.sock remove 1 ``` -You can also expose ports using `socat` and `nsenter` instead of RootlessKit's port drivers. -```console -$ pid=$(cat /run/user/1001/rootlesskit/foo/child_pid) -$ socat -t -- TCP-LISTEN:8080,reuseaddr,fork EXEC:"nsenter -U -n -t $pid socat -t -- STDIN TCP4\:127.0.0.1\:80" -``` ### Exposing privileged ports To expose privileged ports (< 1024), add `net.ipv4.ip_unprivileged_port_start=0` to `/etc/sysctl.conf` (or `/etc/sysctl.d`) and run `sudo sysctl --system`. diff --git a/go.mod b/go.mod index 6e72ec21..94f66ca2 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.24.0 require ( github.com/Masterminds/semver/v3 v3.4.0 github.com/containernetworking/plugins v1.7.1 - github.com/containers/gvisor-tap-vsock v0.8.7 + github.com/containers/gvisor-tap-vsock v0.8.8-0.20250909141233-c4615cdb1462 github.com/gofrs/flock v0.12.1 github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 @@ -40,6 +40,3 @@ require ( golang.org/x/tools v0.36.0 // indirect gvisor.dev/gvisor v0.0.0-20240916094835-a174eb65023f // indirect ) - -// Point to upstream main until the next release to get this fix: https://github.com/containers/gvisor-tap-vsock/commit/c4615cdb1462cb4c9ea5ab770ea11ad03c472c26 -replace github.com/containers/gvisor-tap-vsock => github.com/containers/gvisor-tap-vsock v0.8.8-0.20250909141233-c4615cdb1462 From ce86985ade8640428b8af72df6af37c6351fb750 Mon Sep 17 00:00:00 2001 From: fahed dorgaa Date: Mon, 22 Sep 2025 17:25:24 +0200 Subject: [PATCH 5/5] port.md: update benchmark description for gvisor-tap-vsock driver Signed-off-by: fahed dorgaa --- docs/port.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/port.md b/docs/port.md index 6d0d2962..bc17e7fb 100644 --- a/docs/port.md +++ b/docs/port.md @@ -10,7 +10,7 @@ The default value is `none` (do not expose ports). | `builtin` | 35.6 Gbps | Always 127.0.0.1 | | `gvisor-tap-vsock` (Experimental) | 3.99 Gbps | Propagated | Throughput is currently limited; see issue link below for improvement ideas. -([Benchmark: iperf3 from the parent to the child (Mar 8, 2020)](https://github.com/rootless-containers/rootlesskit/runs/492498728)) +Benchmark: iperf3 from the parent to the child is measured on GitHub Actions The `builtin` driver is fast, but be aware that the source IP is not propagated and always set to 127.0.0.1.