github.com/rootless-containers/rootlesskit/v2@v2.3.4/pkg/network/vpnkit/vpnkit.go (about)

     1  package vpnkit
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"net"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"strconv"
    13  	"sync"
    14  	"syscall"
    15  	"time"
    16  
    17  	"github.com/containernetworking/plugins/pkg/ns"
    18  	"github.com/google/uuid"
    19  	"github.com/moby/vpnkit/go/pkg/vmnet"
    20  
    21  	"github.com/sirupsen/logrus"
    22  	"github.com/songgao/water"
    23  
    24  	"github.com/rootless-containers/rootlesskit/v2/pkg/api"
    25  	"github.com/rootless-containers/rootlesskit/v2/pkg/common"
    26  	"github.com/rootless-containers/rootlesskit/v2/pkg/messages"
    27  	"github.com/rootless-containers/rootlesskit/v2/pkg/network"
    28  )
    29  
    30  func NewParentDriver(binary string, mtu int, ifname string, disableHostLoopback bool) network.ParentDriver {
    31  	if binary == "" {
    32  		panic("got empty vpnkit binary")
    33  	}
    34  	if mtu < 0 {
    35  		panic("got negative mtu")
    36  	}
    37  	if mtu == 0 {
    38  		mtu = 1500
    39  	}
    40  	if mtu != 1500 {
    41  		logrus.Warnf("vpnkit is known to have issues with non-1500 MTU (current: %d), see https://github.com/rootless-containers/rootlesskit/issues/6#issuecomment-403531453", mtu)
    42  		// NOTE: iperf3 stops working with MTU >= 16425
    43  	}
    44  	if ifname == "" {
    45  		ifname = "tap0"
    46  	}
    47  	return &parentDriver{
    48  		binary:              binary,
    49  		mtu:                 mtu,
    50  		ifname:              ifname,
    51  		disableHostLoopback: disableHostLoopback,
    52  	}
    53  }
    54  
    55  const (
    56  	DriverName   = "vpnkit"
    57  	opaqueMAC    = "vpnkit.mac"
    58  	opaqueSocket = "vpnkit.socket"
    59  	opaqueUUID   = "vpnkit.uuid"
    60  )
    61  
    62  type parentDriver struct {
    63  	binary              string
    64  	mtu                 int
    65  	ifname              string
    66  	disableHostLoopback bool
    67  	infoMu              sync.RWMutex
    68  	info                func() *api.NetworkDriverInfo
    69  }
    70  
    71  func (d *parentDriver) Info(ctx context.Context) (*api.NetworkDriverInfo, error) {
    72  	d.infoMu.RLock()
    73  	infoFn := d.info
    74  	d.infoMu.RUnlock()
    75  	if infoFn == nil {
    76  		return &api.NetworkDriverInfo{
    77  			Driver: DriverName,
    78  		}, nil
    79  	}
    80  
    81  	return infoFn(), nil
    82  }
    83  
    84  func (d *parentDriver) MTU() int {
    85  	return d.mtu
    86  }
    87  
    88  func (d *parentDriver) ConfigureNetwork(childPID int, stateDir, detachedNetNSPath string) (*messages.ParentInitNetworkDriverCompleted, func() error, error) {
    89  	var cleanups []func() error
    90  	vpnkitSocket := filepath.Join(stateDir, "vpnkit-ethernet.sock")
    91  	vpnkitCtx, vpnkitCancel := context.WithCancel(context.Background())
    92  	vpnkitCmd := exec.CommandContext(vpnkitCtx, d.binary, "--ethernet", vpnkitSocket, "--mtu", strconv.Itoa(d.mtu))
    93  	if d.disableHostLoopback {
    94  		vpnkitCmd.Args = append(vpnkitCmd.Args, "--host-ip", "0.0.0.0")
    95  	}
    96  	vpnkitCmd.SysProcAttr = &syscall.SysProcAttr{
    97  		Pdeathsig: syscall.SIGKILL,
    98  	}
    99  	cleanups = append(cleanups, func() error {
   100  		logrus.Debugf("killing vpnkit")
   101  		vpnkitCancel()
   102  		wErr := vpnkitCmd.Wait()
   103  		logrus.Debugf("killed vpnkit: %v", wErr)
   104  		return nil
   105  	})
   106  	if err := vpnkitCmd.Start(); err != nil {
   107  		return nil, common.Seq(cleanups), fmt.Errorf("executing %v: %w", vpnkitCmd, err)
   108  	}
   109  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   110  	cleanups = append(cleanups, func() error { cancel(); return nil })
   111  	vmnet, err := waitForVPNKit(ctx, vpnkitSocket)
   112  	if err != nil {
   113  		return nil, common.Seq(cleanups), fmt.Errorf("connecting to %s: %w", vpnkitSocket, err)
   114  	}
   115  	cleanups = append(cleanups, func() error { return vmnet.Close() })
   116  	vifUUID := uuid.New()
   117  	logrus.Debugf("connecting to VPNKit vmnet at %s as %s", vpnkitSocket, vifUUID)
   118  	// No context.WithTimeout..?
   119  	vif, err := vmnet.ConnectVif(vifUUID)
   120  	if err != nil {
   121  		return nil, common.Seq(cleanups), fmt.Errorf("connecting to %s with uuid %s: %w", vpnkitSocket, vifUUID, err)
   122  	}
   123  	logrus.Debugf("connected to VPNKit vmnet")
   124  	// TODO: support configuration
   125  	netmsg := messages.ParentInitNetworkDriverCompleted{
   126  		Dev:     d.ifname,
   127  		IP:      vif.IP.String(),
   128  		Netmask: 24,
   129  		Gateway: "192.168.65.1",
   130  		DNS:     []string{"192.168.65.1"},
   131  		MTU:     d.mtu,
   132  		NetworkDriverOpaque: map[string]string{
   133  			opaqueMAC:    vif.ClientMAC.String(),
   134  			opaqueSocket: vpnkitSocket,
   135  			opaqueUUID:   vifUUID.String(),
   136  		},
   137  	}
   138  	d.infoMu.Lock()
   139  	d.info = func() *api.NetworkDriverInfo {
   140  		return &api.NetworkDriverInfo{
   141  			Driver:         DriverName,
   142  			DNS:            []net.IP{net.ParseIP(netmsg.DNS[0])},
   143  			ChildIP:        net.ParseIP(netmsg.IP),
   144  			DynamicChildIP: false,
   145  		}
   146  	}
   147  	d.infoMu.Unlock()
   148  	return &netmsg, common.Seq(cleanups), nil
   149  }
   150  
   151  func waitForVPNKit(ctx context.Context, socket string) (*vmnet.Vmnet, error) {
   152  	retried := 0
   153  	for {
   154  		vmnet, err := vmnet.New(ctx, socket)
   155  		if err == nil {
   156  			return vmnet, nil
   157  		}
   158  		sleepTime := (retried % 100) * 10 * int(time.Microsecond)
   159  		select {
   160  		case <-ctx.Done():
   161  			return nil, fmt.Errorf("last error: %v: %w", err, ctx.Err())
   162  		case <-time.After(time.Duration(sleepTime)):
   163  		}
   164  		retried++
   165  	}
   166  }
   167  
   168  func NewChildDriver() network.ChildDriver {
   169  	return &childDriver{}
   170  }
   171  
   172  type childDriver struct {
   173  }
   174  
   175  func (d *childDriver) ChildDriverInfo() (*network.ChildDriverInfo, error) {
   176  	return &network.ChildDriverInfo{
   177  		ConfiguresInterface: false,
   178  	}, nil
   179  }
   180  
   181  func (d *childDriver) ConfigureNetworkChild(netmsg *messages.ParentInitNetworkDriverCompleted, detachedNetNSPath string) (tap string, err error) {
   182  	tapName := netmsg.Dev
   183  	if tapName == "" {
   184  		return "", errors.New("no dev is set")
   185  	}
   186  	macStr := netmsg.NetworkDriverOpaque[opaqueMAC]
   187  	socket := netmsg.NetworkDriverOpaque[opaqueSocket]
   188  	uuidStr := netmsg.NetworkDriverOpaque[opaqueUUID]
   189  	if macStr == "" {
   190  		return "", errors.New("no VPNKit MAC is set")
   191  	}
   192  	if socket == "" {
   193  		return "", errors.New("no VPNKit socket is set")
   194  	}
   195  	if uuidStr == "" {
   196  		return "", errors.New("no VPNKit UUID is set")
   197  	}
   198  	return startVPNKitRoutines(context.TODO(), tapName, macStr, socket, uuidStr, detachedNetNSPath)
   199  }
   200  
   201  func startVPNKitRoutines(ctx context.Context, tapName, macStr, socket, uuidStr, detachedNetNSPath string) (string, error) {
   202  	var tap *water.Interface
   203  	fn := func(_ ns.NetNS) error {
   204  		cmds := [][]string{
   205  			{"ip", "tuntap", "add", "name", tapName, "mode", "tap"},
   206  			{"ip", "link", "set", tapName, "address", macStr},
   207  			// IP stuff and MTU are configured in activateTap() in pkg/child/child.go
   208  		}
   209  		if err := common.Execs(os.Stderr, os.Environ(), cmds); err != nil {
   210  			return fmt.Errorf("executing %v: %w", cmds, err)
   211  		}
   212  		var err error
   213  		tap, err = water.New(
   214  			water.Config{
   215  				DeviceType: water.TAP,
   216  				PlatformSpecificParams: water.PlatformSpecificParams{
   217  					Name: tapName,
   218  				},
   219  			})
   220  		if err != nil {
   221  			return fmt.Errorf("creating tap %s: %w", tapName, err)
   222  		}
   223  		return nil
   224  	}
   225  	if detachedNetNSPath == "" {
   226  		if err := fn(nil); err != nil {
   227  			return "", err
   228  		}
   229  	} else {
   230  		if err := ns.WithNetNSPath(detachedNetNSPath, fn); err != nil {
   231  			return "", err
   232  		}
   233  	}
   234  	if tap.Name() != tapName {
   235  		return "", fmt.Errorf("expected %q, got %q", tapName, tap.Name())
   236  	}
   237  	vmnet, err := vmnet.New(ctx, socket)
   238  	if err != nil {
   239  		return "", err
   240  	}
   241  	vifUUID, err := uuid.Parse(uuidStr)
   242  	if err != nil {
   243  		return "", err
   244  	}
   245  	vif, err := vmnet.ConnectVif(vifUUID)
   246  	if err != nil {
   247  		return "", err
   248  	}
   249  	go tap2vif(vif, tap)
   250  	go vif2tap(tap, vif)
   251  	return tapName, nil
   252  }
   253  
   254  func tap2vif(vif *vmnet.Vif, r io.Reader) {
   255  	b := make([]byte, 65536)
   256  	for {
   257  		n, err := r.Read(b)
   258  		if err != nil {
   259  			if errors.Is(err, io.EOF) {
   260  				return
   261  			}
   262  			panic(fmt.Errorf("tap2vif: read: %w", err))
   263  		}
   264  		if err := vif.Write(b[:n]); err != nil {
   265  			if errors.Is(err, io.EOF) {
   266  				return
   267  			}
   268  			panic(fmt.Errorf("tap2vif: write: %w", err))
   269  		}
   270  	}
   271  }
   272  
   273  func vif2tap(w io.Writer, vif *vmnet.Vif) {
   274  	for {
   275  		b, err := vif.Read()
   276  		if err != nil {
   277  			if errors.Is(err, io.EOF) {
   278  				return
   279  			}
   280  			panic(fmt.Errorf("vif2tap: read: %w", err))
   281  		}
   282  		if _, err := w.Write(b); err != nil {
   283  			if errors.Is(err, io.EOF) {
   284  				return
   285  			}
   286  
   287  			panic(fmt.Errorf("vif2tap: write: %w", err))
   288  		}
   289  	}
   290  }