// 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. //go:build !js // Package tun creates a tuntap device, working around OS-specific // quirks if necessary. package tstun import ( "errors" "runtime" "strings" "time" "github.com/tailscale/wireguard-go/tun" "tailscale.com/envknob" "tailscale.com/types/logger" ) // 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 disableTUNOffload = envknob.Bool("TS_DISABLE_TUN_OFFLOAD") 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") } if createTAP == nil { // if the ts_omit_tap tag is used return nil, "", errors.New("tap is not supported in this build") } 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 { tunMTU := DefaultMTU if mtu, ok := envknob.LookupInt("TS_DEBUG_MTU"); ok { tunMTU = mtu } dev, err = tun.CreateTUN(tunName, tunMTU) if err == nil && disableTUNOffload { if do, ok := dev.(tun.DisableOffloader); ok { do.DisableOffload() } } } if err != nil { return nil, "", err } if err := waitInterfaceUp(dev, 90*time.Second, logf); err != nil { dev.Close() return nil, "", err } if err := setLinkAttrs(dev); err != nil { logf("setting link attributes: %v", 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, err error) // 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. // // The tunName is the name of the tun device that was requested but failed. // The err error is how the tun creation failed. func Diagnose(logf logger.Logf, tunName string, err error) { if tunDiagnoseFailure != nil { tunDiagnoseFailure(tunName, logf, err) } else { logf("no TUN failure diagnostics for OS %q", runtime.GOOS) } }