Skip to content

Commit

Permalink
This adds stdio as a way to communicate between gvproxy and vm mainly…
Browse files Browse the repository at this point in the history
… for use

with WSL2, although it should work for other cases as well.

When network connections between WSL2 and the Windows host are blocked, stdio
is the only reliable way to establish a channel between WSL2 and the Windows
host. Hyper-V socket for WSL2 is a possibility, but it requires undocumented
APIs and admin privileges.

Signed-off-by: Keiichi Shimamura <sakai135@users.noreply.github.com>
  • Loading branch information
sakai135 committed Feb 14, 2023
1 parent e1d9f2c commit 22b319b
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 7 deletions.
13 changes: 12 additions & 1 deletion cmd/gvproxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"syscall"
"time"

"github.com/containers/gvisor-tap-vsock/pkg/net/stdio"
"github.com/containers/gvisor-tap-vsock/pkg/sshclient"
"github.com/containers/gvisor-tap-vsock/pkg/transport"
"github.com/containers/gvisor-tap-vsock/pkg/types"
Expand All @@ -34,6 +35,7 @@ var (
vpnkitSocket string
qemuSocket string
bessSocket string
stdioSocket string
forwardSocket arrayFlags
forwardDest arrayFlags
forwardUser arrayFlags
Expand All @@ -56,12 +58,14 @@ func main() {
flag.StringVar(&vpnkitSocket, "listen-vpnkit", "", "VPNKit socket to be used by Hyperkit")
flag.StringVar(&qemuSocket, "listen-qemu", "", "Socket to be used by Qemu")
flag.StringVar(&bessSocket, "listen-bess", "", "unixpacket socket to be used by Bess-compatible applications")
flag.StringVar(&stdioSocket, "listen-stdio", "", "accept stdio pipe")
flag.Var(&forwardSocket, "forward-sock", "Forwards a unix socket to the guest virtual machine over SSH")
flag.Var(&forwardDest, "forward-dest", "Forwards a unix socket to the guest virtual machine over SSH")
flag.Var(&forwardUser, "forward-user", "SSH user to use for unix socket forward")
flag.Var(&forwardIdentify, "forward-identity", "Path to SSH identity key for forwarding")
flag.StringVar(&pidFile, "pid-file", "", "Generate a file with the PID in it")
flag.Parse()

ctx, cancel := context.WithCancel(context.Background())
// Make this the last defer statement in the stack
defer os.Exit(exitCode)
Expand Down Expand Up @@ -275,7 +279,7 @@ func run(ctx context.Context, g *errgroup.Group, configuration *types.Configurat
for {
select {
case <-time.After(5 * time.Second):
fmt.Printf("%v sent to the VM, %v received from the VM\n", humanize.Bytes(vn.BytesSent()), humanize.Bytes(vn.BytesReceived()))
log.Debugf("%v sent to the VM, %v received from the VM\n", humanize.Bytes(vn.BytesSent()), humanize.Bytes(vn.BytesReceived()))
case <-ctx.Done():
break debugLog
}
Expand Down Expand Up @@ -359,6 +363,13 @@ func run(ctx context.Context, g *errgroup.Group, configuration *types.Configurat
})
}

if stdioSocket != "" {
g.Go(func() error {
conn := stdio.GetStdioConn()
return vn.AcceptQemu(ctx, conn)
})
}

for i := 0; i < len(forwardSocket); i++ {
var (
src *url.URL
Expand Down
14 changes: 8 additions & 6 deletions cmd/vm/main_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,14 @@ func run() error {
}
defer conn.Close()

req, err := http.NewRequest("POST", path, nil)
if err != nil {
return err
}
if err := req.Write(conn); err != nil {
return err
if path != "" {
req, err := http.NewRequest("POST", path, nil)
if err != nil {
return err
}
if err := req.Write(conn); err != nil {
return err
}
}

tap, err := water.New(water.Config{
Expand Down
51 changes: 51 additions & 0 deletions pkg/net/stdio/dial.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package stdio

import (
"net"
"os"
"os/exec"
"strconv"
)

func Dial(endpoint string, arg ...string) (net.Conn, error) {
cmd := exec.Command(endpoint, arg...)
cmd.Stderr = os.Stderr

stdin, err := cmd.StdinPipe()
if err != nil {
return nil, err
}

stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}

err = cmd.Start()
if err != nil {
return nil, err
}

local := IoAddr{path: strconv.Itoa(os.Getpid())}
remote := IoAddr{path: strconv.Itoa(cmd.Process.Pid)}
conn := IoConn{
reader: stdout,
writer: stdin,
local: local,
remote: remote,
close: cmd.Process.Kill,
}
return conn, nil
}

func GetStdioConn() net.Conn {
local := IoAddr{path: strconv.Itoa(os.Getpid())}
remote := IoAddr{path: "remote"}
conn := IoConn{
writer: os.Stdout,
reader: os.Stdin,
local: local,
remote: remote,
}
return conn
}
12 changes: 12 additions & 0 deletions pkg/net/stdio/ioaddr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package stdio

type IoAddr struct {
path string
}

func (a IoAddr) Network() string {
return "stdio"
}
func (a IoAddr) String() string {
return a.path
}
50 changes: 50 additions & 0 deletions pkg/net/stdio/ioconn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package stdio

import (
"io"
"net"
"time"
)

type IoConn struct {
writer io.Writer
reader io.Reader
local net.Addr
remote net.Addr
close func() error
}

func (c IoConn) Read(b []byte) (n int, err error) {
return c.reader.Read(b)
}

func (c IoConn) Write(b []byte) (n int, err error) {
return c.writer.Write(b)
}

func (c IoConn) Close() error {
if c.close != nil {
return c.close()
}
return nil
}

func (c IoConn) LocalAddr() net.Addr {
return c.local
}

func (c IoConn) RemoteAddr() net.Addr {
return c.remote
}

func (c IoConn) SetDeadline(t time.Time) error {
return nil
}

func (c IoConn) SetReadDeadline(t time.Time) error {
return nil
}

func (c IoConn) SetWriteDeadline(t time.Time) error {
return nil
}
11 changes: 11 additions & 0 deletions pkg/transport/dial_linux.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package transport

import (
"fmt"
"net"
"net/url"
"strconv"

"github.com/containers/gvisor-tap-vsock/pkg/net/stdio"
mdlayhervsock "github.com/mdlayher/vsock"
"github.com/pkg/errors"
)
Expand All @@ -29,6 +31,15 @@ func Dial(endpoint string) (net.Conn, string, error) {
case "unix":
conn, err := net.Dial("unix", parsed.Path)
return conn, "/connect", err
case "stdio":
var values []string
for k, vs := range parsed.Query() {
for _, v := range vs {
values = append(values, fmt.Sprintf("-%s=%s", k, v))
}
}
conn, err := stdio.Dial(parsed.Path, values...)
return conn, "", err
default:
return nil, "", errors.New("unexpected scheme")
}
Expand Down
9 changes: 9 additions & 0 deletions test/wsl.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash

set -x

./bin/vm \
-url="stdio:$(pwd)/bin/gvproxy-windows.exe?listen-stdio=accept&debug=true" \
-iface="eth1" \
-stop-if-exist="" \
-debug

0 comments on commit 22b319b

Please sign in to comment.