// 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 // +build !js // Package tun creates a tuntap device, working around OS-specific // quirks if necessary. package tstun import ( "errors" "runtime" "strings" "time" "golang.zx2c4.com/wireguard/tun" "tailscale.com/envknob" "tailscale.com/types/logger" ) var tunMTU = DefaultMTU func init() { if mtu, ok := envknob.LookupInt("TS_DEBUG_MTU"); ok { 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 } 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) // 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) } }