tailscale/net/tstun/tun.go

93 lines
2.5 KiB
Go

// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package tun creates a tuntap device, working around OS-specific
// quirks if necessary.
package tstun
import (
"errors"
"os"
"runtime"
"strconv"
"strings"
"time"
"golang.zx2c4.com/wireguard/tun"
"tailscale.com/types/logger"
)
// tunMTU is the MTU we set on tailscale's TUN interface. wireguard-go
// defaults to 1420 bytes, which only works if the "outer" MTU is 1500
// bytes. This breaks on DSL connections (typically 1492 MTU) and on
// GCE (1460 MTU?!).
//
// 1280 is the smallest MTU allowed for IPv6, which is a sensible
// "probably works everywhere" setting until we develop proper PMTU
// discovery.
var tunMTU = 1280
func init() {
if mtu, _ := strconv.Atoi(os.Getenv("TS_DEBUG_MTU")); mtu != 0 {
tunMTU = mtu
}
}
// createTAP is non-nil on Linux.
var createTAP func(tapName, bridgeName string) (tun.Device, error)
// New returns a tun.Device for the requested device name, along with
// the OS-dependent name that was allocated to the device.
func New(logf logger.Logf, tunName string) (tun.Device, string, error) {
var dev tun.Device
var err error
if strings.HasPrefix(tunName, "tap:") {
if runtime.GOOS != "linux" {
return nil, "", errors.New("tap only works on Linux")
}
f := strings.Split(tunName, ":")
var tapName, bridgeName string
switch len(f) {
case 2:
tapName = f[1]
case 3:
tapName, bridgeName = f[1], f[2]
default:
return nil, "", errors.New("bogus tap argument")
}
dev, err = createTAP(tapName, bridgeName)
} else {
dev, err = tun.CreateTUN(tunName, tunMTU)
}
if err != nil {
return nil, "", err
}
if err := waitInterfaceUp(dev, 90*time.Second, logf); err != nil {
dev.Close()
return nil, "", err
}
name, err := interfaceName(dev)
if err != nil {
dev.Close()
return nil, "", err
}
return dev, name, nil
}
// tunDiagnoseFailure, if non-nil, does OS-specific diagnostics of why
// TUN failed to work.
var tunDiagnoseFailure func(tunName string, logf logger.Logf)
// Diagnose tries to explain a tuntap device creation failure.
// It pokes around the system and logs some diagnostic info that might
// help debug why tun creation failed. Because device creation has
// already failed and the program's about to end, log a lot.
func Diagnose(logf logger.Logf, tunName string) {
if tunDiagnoseFailure != nil {
tunDiagnoseFailure(tunName, logf)
} else {
logf("no TUN failure diagnostics for OS %q", runtime.GOOS)
}
}