2020-02-05 22:16:58 +00:00
// Copyright (c) 2020 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 wgengine
import (
"bufio"
2020-04-08 15:42:38 +00:00
"bytes"
2020-02-25 16:06:29 +00:00
"context"
2020-02-05 22:16:58 +00:00
"fmt"
2020-04-08 15:42:38 +00:00
"io"
2020-02-05 22:16:58 +00:00
"log"
2020-02-25 16:06:29 +00:00
"net"
2020-04-10 20:44:08 +00:00
"os"
"os/exec"
"runtime"
2020-02-05 22:16:58 +00:00
"strings"
"sync"
"time"
"github.com/tailscale/wireguard-go/device"
"github.com/tailscale/wireguard-go/tun"
"github.com/tailscale/wireguard-go/wgcfg"
2020-04-08 15:42:38 +00:00
"go4.org/mem"
2020-03-26 05:57:46 +00:00
"tailscale.com/ipn/ipnstate"
2020-03-13 03:10:11 +00:00
"tailscale.com/net/interfaces"
2020-02-05 22:16:58 +00:00
"tailscale.com/tailcfg"
2020-03-26 05:57:46 +00:00
"tailscale.com/types/key"
2020-02-15 03:23:16 +00:00
"tailscale.com/types/logger"
2020-02-05 22:16:58 +00:00
"tailscale.com/wgengine/filter"
"tailscale.com/wgengine/magicsock"
2020-02-17 17:00:38 +00:00
"tailscale.com/wgengine/monitor"
2020-02-05 22:16:58 +00:00
"tailscale.com/wgengine/packet"
)
type userspaceEngine struct {
2020-02-28 17:32:06 +00:00
logf logger . Logf
reqCh chan struct { }
waitCh chan struct { }
tundev tun . Device
wgdev * device . Device
router Router
magicConn * magicsock . Conn
linkMon * monitor . Mon
2020-02-05 22:16:58 +00:00
2020-03-25 15:40:36 +00:00
wgLock sync . Mutex // serializes all wgdev operations; see lock order comment below
2020-02-05 22:16:58 +00:00
lastReconfig string
2020-02-25 16:06:29 +00:00
lastCfg wgcfg . Config
2020-02-05 22:16:58 +00:00
lastRoutes string
2020-03-25 15:40:36 +00:00
mu sync . Mutex // guards following; see lock order comment below
filt * filter . Filter
2020-02-28 17:32:06 +00:00
statusCallback StatusCallback
peerSequence [ ] wgcfg . Key
endpoints [ ] string
pingers map [ wgcfg . Key ] context . CancelFunc // mu must be held to call CancelFunc
2020-03-13 03:10:11 +00:00
linkState * interfaces . State
2020-03-25 15:40:36 +00:00
// Lock ordering: wgLock, then mu.
2020-02-05 22:16:58 +00:00
}
type Loggify struct {
f logger . Logf
}
func ( l * Loggify ) Write ( b [ ] byte ) ( int , error ) {
l . f ( string ( b ) )
return len ( b ) , nil
}
2020-02-14 23:03:25 +00:00
func NewFakeUserspaceEngine ( logf logger . Logf , listenPort uint16 ) ( Engine , error ) {
2020-02-05 22:16:58 +00:00
logf ( "Starting userspace wireguard engine (FAKE tuntap device)." )
tun := NewFakeTun ( )
2020-02-14 23:03:25 +00:00
return NewUserspaceEngineAdvanced ( logf , tun , NewFakeRouter , listenPort )
2020-02-05 22:16:58 +00:00
}
2020-02-14 23:03:25 +00:00
// NewUserspaceEngine creates the named tun device and returns a Tailscale Engine
// running on it.
func NewUserspaceEngine ( logf logger . Logf , tunname string , listenPort uint16 ) ( Engine , error ) {
2020-02-05 22:16:58 +00:00
if tunname == "" {
2020-02-11 08:01:58 +00:00
return nil , fmt . Errorf ( "--tun name must not be blank" )
2020-02-05 22:16:58 +00:00
}
2020-04-10 20:44:08 +00:00
logf ( "Starting userspace wireguard engine with tun device %q" , tunname )
2020-02-14 23:03:25 +00:00
tundev , err := tun . CreateTUN ( tunname , device . DefaultMTU )
2020-02-05 22:16:58 +00:00
if err != nil {
2020-04-10 20:44:08 +00:00
diagnoseTUNFailure ( logf )
2020-04-11 15:35:34 +00:00
logf ( "CreateTUN: %v" , err )
2020-02-05 22:16:58 +00:00
return nil , err
}
2020-04-11 15:35:34 +00:00
logf ( "CreateTUN ok." )
2020-02-05 22:16:58 +00:00
2020-02-14 23:03:25 +00:00
e , err := NewUserspaceEngineAdvanced ( logf , tundev , newUserspaceRouter , listenPort )
2020-02-05 22:16:58 +00:00
if err != nil {
2020-04-11 15:35:34 +00:00
logf ( "NewUserspaceEngineAdv: %v" , err )
2020-02-14 23:03:25 +00:00
tundev . Close ( )
2020-02-05 22:16:58 +00:00
return nil , err
}
return e , err
}
2020-02-14 23:03:25 +00:00
// NewUserspaceEngineAdvanced is like NewUserspaceEngine but takes a pre-created TUN device and allows specifing
// a custom router constructor and listening port.
func NewUserspaceEngineAdvanced ( logf logger . Logf , tundev tun . Device , routerGen RouterGen , listenPort uint16 ) ( Engine , error ) {
return newUserspaceEngineAdvanced ( logf , tundev , routerGen , listenPort )
}
2020-02-05 22:16:58 +00:00
2020-02-14 23:03:25 +00:00
func newUserspaceEngineAdvanced ( logf logger . Logf , tundev tun . Device , routerGen RouterGen , listenPort uint16 ) ( _ Engine , reterr error ) {
2020-02-05 22:16:58 +00:00
e := & userspaceEngine {
2020-02-25 16:06:29 +00:00
logf : logf ,
reqCh : make ( chan struct { } , 1 ) ,
waitCh : make ( chan struct { } ) ,
tundev : tundev ,
pingers : make ( map [ wgcfg . Key ] context . CancelFunc ) ,
2020-02-05 22:16:58 +00:00
}
2020-03-13 03:10:11 +00:00
e . linkState , _ = getLinkState ( )
2020-02-05 22:16:58 +00:00
2020-02-17 17:00:38 +00:00
mon , err := monitor . New ( logf , func ( ) { e . LinkChange ( false ) } )
if err != nil {
return nil , err
}
e . linkMon = mon
2020-02-05 22:16:58 +00:00
endpointsFn := func ( endpoints [ ] string ) {
e . mu . Lock ( )
2020-02-18 16:57:11 +00:00
e . endpoints = append ( e . endpoints [ : 0 ] , endpoints ... )
2020-02-05 22:16:58 +00:00
e . mu . Unlock ( )
e . RequestStatus ( )
}
magicsockOpts := magicsock . Options {
2020-02-18 16:57:11 +00:00
Port : listenPort ,
2020-02-05 22:16:58 +00:00
EndpointsFunc : endpointsFn ,
}
e . magicConn , err = magicsock . Listen ( magicsockOpts )
if err != nil {
return nil , fmt . Errorf ( "wgengine: %v" , err )
}
// flags==0 because logf is already nested in another logger.
// The outer one can display the preferred log prefixes, etc.
dlog := log . New ( & Loggify { logf } , "" , 0 )
logger := device . Logger {
Debug : dlog ,
Info : dlog ,
Error : dlog ,
}
nofilter := func ( b [ ] byte ) device . FilterResult {
// for safety, default to dropping all packets
2020-04-11 15:35:34 +00:00
logf ( "Warning: you forgot to use wgengine.SetFilterInOut()! Packet dropped." )
2020-02-05 22:16:58 +00:00
return device . FilterDrop
}
opts := & device . DeviceOptions {
Logger : & logger ,
FilterIn : nofilter ,
FilterOut : nofilter ,
2020-02-25 16:06:29 +00:00
HandshakeDone : func ( peerKey wgcfg . Key , allowedIPs [ ] net . IPNet ) {
2020-02-05 22:16:58 +00:00
// Send an unsolicited status event every time a
// handshake completes. This makes sure our UI can
// update quickly as soon as it connects to a peer.
//
// We use a goroutine here to avoid deadlocking
// wireguard, since RequestStatus() will call back
// into it, and wireguard is what called us to get
// here.
go e . RequestStatus ( )
2020-02-25 16:06:29 +00:00
2020-03-08 11:08:38 +00:00
// Ping every single-IP that peer routes.
// These synthetic packets are used to traverse NATs.
var ips [ ] wgcfg . IP
for _ , ipNet := range allowedIPs {
if ones , bits := ipNet . Mask . Size ( ) ; ones == bits && ones != 0 {
2020-02-25 16:06:29 +00:00
var ip wgcfg . IP
2020-03-08 11:08:38 +00:00
copy ( ip . Addr [ : ] , ipNet . IP . To16 ( ) )
ips = append ( ips , ip )
2020-02-25 16:06:29 +00:00
}
}
2020-03-08 11:08:38 +00:00
if len ( ips ) > 0 {
go e . pinger ( peerKey , ips )
} else {
logf ( "[unexpected] peer %s has no single-IP routes: %v" , peerKey . ShortString ( ) , allowedIPs )
}
2020-02-05 22:16:58 +00:00
} ,
2020-03-03 15:39:02 +00:00
CreateBind : e . magicConn . CreateBind ,
2020-02-05 22:16:58 +00:00
CreateEndpoint : e . magicConn . CreateEndpoint ,
SkipBindUpdate : true ,
}
2020-02-14 23:03:25 +00:00
e . wgdev = device . NewDevice ( e . tundev , opts )
defer func ( ) {
if reterr != nil {
e . wgdev . Close ( )
}
} ( )
2020-02-17 17:00:38 +00:00
e . router , err = routerGen ( logf , e . wgdev , e . tundev )
2020-02-14 23:03:25 +00:00
if err != nil {
return nil , err
}
2020-02-05 22:16:58 +00:00
go func ( ) {
up := false
2020-02-14 23:03:25 +00:00
for event := range e . tundev . Events ( ) {
2020-02-05 22:16:58 +00:00
if event & tun . EventMTUUpdate != 0 {
2020-02-14 23:03:25 +00:00
mtu , err := e . tundev . MTU ( )
2020-02-05 22:16:58 +00:00
e . logf ( "external route MTU: %d (%v)" , mtu , err )
}
if event & tun . EventUp != 0 && ! up {
e . logf ( "external route: up" )
e . RequestStatus ( )
up = true
}
if event & tun . EventDown != 0 && up {
e . logf ( "external route: down" )
e . RequestStatus ( )
up = false
}
}
} ( )
e . wgdev . Up ( )
if err := e . router . Up ( ) ; err != nil {
e . wgdev . Close ( )
return nil , err
}
2020-02-13 16:59:31 +00:00
if err := e . router . SetRoutes ( RouteSettings { Cfg : new ( wgcfg . Config ) } ) ; err != nil {
2020-02-05 22:16:58 +00:00
e . wgdev . Close ( )
return nil , err
}
2020-02-17 17:00:38 +00:00
e . linkMon . Start ( )
2020-02-05 22:16:58 +00:00
return e , nil
}
2020-02-28 11:30:46 +00:00
// pinger sends ping packets for a few seconds.
2020-02-25 16:06:29 +00:00
//
// These generated packets are used to ensure we trigger the spray logic in
// the magicsock package for NAT traversal.
2020-03-08 11:08:38 +00:00
func ( e * userspaceEngine ) pinger ( peerKey wgcfg . Key , ips [ ] wgcfg . IP ) {
e . logf ( "generating initial ping traffic to %s (%v)" , peerKey . ShortString ( ) , ips )
2020-02-25 16:06:29 +00:00
var srcIP packet . IP
e . wgLock . Lock ( )
if len ( e . lastCfg . Addresses ) > 0 {
srcIP = packet . NewIP ( e . lastCfg . Addresses [ 0 ] . IP . IP ( ) )
}
e . wgLock . Unlock ( )
if srcIP == 0 {
e . logf ( "generating initial ping traffic: no source IP" )
return
}
e . mu . Lock ( )
if cancel := e . pingers [ peerKey ] ; cancel != nil {
cancel ( )
}
ctx , cancel := context . WithCancel ( context . Background ( ) )
e . pingers [ peerKey ] = cancel
e . mu . Unlock ( )
// sendFreq is slightly longer than sprayFreq in magicsock to ensure
// that if these ping packets are the only source of early packets
// sent to the peer, that each one will be sprayed.
const sendFreq = 300 * time . Millisecond
const stopAfter = 3 * time . Second
start := time . Now ( )
2020-03-08 11:08:38 +00:00
var dstIPs [ ] packet . IP
for _ , ip := range ips {
dstIPs = append ( dstIPs , packet . NewIP ( ip . IP ( ) ) )
}
2020-02-25 16:06:29 +00:00
payload := [ ] byte ( "magicsock_spray" ) // no meaning
2020-02-28 11:30:46 +00:00
defer func ( ) {
e . mu . Lock ( )
defer e . mu . Unlock ( )
select {
case <- ctx . Done ( ) :
return
default :
2020-02-25 16:06:29 +00:00
}
2020-02-28 11:30:46 +00:00
// If the pinger context is not done, then the
// CancelFunc is still in the pingers map.
delete ( e . pingers , peerKey )
2020-02-25 16:06:29 +00:00
} ( )
2020-02-28 11:30:46 +00:00
ipid := uint16 ( 1 )
t := time . NewTicker ( sendFreq )
defer t . Stop ( )
for {
select {
case <- ctx . Done ( ) :
return
case <- t . C :
}
if time . Since ( start ) > stopAfter {
return
}
2020-03-08 11:08:38 +00:00
for _ , dstIP := range dstIPs {
b := packet . GenICMP ( srcIP , dstIP , ipid , packet . EchoRequest , 0 , payload )
e . wgdev . SendPacket ( b )
}
2020-02-28 11:30:46 +00:00
ipid ++
}
2020-02-25 16:06:29 +00:00
}
2020-02-05 22:16:58 +00:00
// TODO(apenwarr): dnsDomains really ought to be in wgcfg.Config.
// However, we don't actually ever provide it to wireguard and it's not in
// the traditional wireguard config format. On the other hand, wireguard
// itself doesn't use the traditional 'dns =' setting either.
func ( e * userspaceEngine ) Reconfig ( cfg * wgcfg . Config , dnsDomains [ ] string ) error {
e . wgLock . Lock ( )
defer e . wgLock . Unlock ( )
2020-02-25 16:06:29 +00:00
e . mu . Lock ( )
2020-04-10 15:22:13 +00:00
e . peerSequence = e . peerSequence [ : 0 ]
for _ , p := range cfg . Peers {
e . peerSequence = append ( e . peerSequence , p . PublicKey )
2020-02-05 22:16:58 +00:00
}
2020-02-25 16:06:29 +00:00
e . mu . Unlock ( )
2020-02-05 22:16:58 +00:00
2020-02-11 23:21:24 +00:00
// TODO(apenwarr): get rid of uapi stuff for in-process comms
2020-02-05 22:16:58 +00:00
uapi , err := cfg . ToUAPI ( )
if err != nil {
return err
}
rc := uapi + "\x00" + strings . Join ( dnsDomains , "\x00" )
if rc == e . lastReconfig {
2020-04-10 15:42:34 +00:00
return ErrNoChanges
2020-02-05 22:16:58 +00:00
}
2020-04-10 15:42:34 +00:00
e . logf ( "wgengine: Reconfig: configuring userspace wireguard engine" )
2020-02-05 22:16:58 +00:00
e . lastReconfig = rc
2020-02-25 16:06:29 +00:00
e . lastCfg = cfg . Copy ( )
2020-02-05 22:16:58 +00:00
2020-02-28 19:13:28 +00:00
// Tell magicsock about the new (or initial) private key
// (which is needed by DERP) before wgdev gets it, as wgdev
// will start trying to handshake, which we want to be able to
// go over DERP.
if err := e . magicConn . SetPrivateKey ( cfg . PrivateKey ) ; err != nil {
2020-04-11 15:35:34 +00:00
e . logf ( "wgengine: Reconfig: SetPrivateKey: %v" , err )
2020-02-28 19:13:28 +00:00
}
2020-02-20 17:47:33 +00:00
if err := e . wgdev . Reconfig ( cfg ) ; err != nil {
2020-04-11 15:35:34 +00:00
e . logf ( "wgdev.Reconfig: %v" , err )
2020-02-05 22:16:58 +00:00
return err
}
// TODO(apenwarr): only handling the first local address.
// Currently we never use more than one anyway.
var cidr wgcfg . CIDR
2020-02-19 15:32:25 +00:00
if len ( cfg . Addresses ) > 0 {
cidr = cfg . Addresses [ 0 ]
2020-02-05 22:16:58 +00:00
// TODO(apenwarr): this shouldn't be hardcoded in the client
cidr . Mask = 10 // route the whole cgnat range
}
rs := RouteSettings {
LocalAddr : cidr ,
2020-02-11 23:21:24 +00:00
Cfg : cfg ,
2020-02-19 15:32:25 +00:00
DNS : cfg . DNS ,
2020-02-05 22:16:58 +00:00
DNSDomains : dnsDomains ,
}
// TODO(apenwarr): all the parts of RouteSettings should be "relevant."
// We're checking only the "relevant" parts to see if they have
// changed, and if not, skipping SetRoutes(). But if SetRoutes()
// is getting the non-relevant parts of Cfg, it might act on them,
// and this optimization is unsafe. Probably we should not pass
// a whole Cfg object as part of RouteSettings; instead, trim it to
// just what's absolutely needed (the set of actual routes).
rss := rs . OnlyRelevantParts ( )
2020-03-02 22:54:57 +00:00
if rss != e . lastRoutes {
2020-04-10 15:42:34 +00:00
e . logf ( "wgengine: Reconfig: reconfiguring router. la=%v dns=%v dom=%v; new routes: %v" ,
2020-03-02 22:54:57 +00:00
rs . LocalAddr , rs . DNS , rs . DNSDomains , rss )
e . lastRoutes = rss
err = e . router . SetRoutes ( rs )
if err != nil {
return err
}
2020-02-05 22:16:58 +00:00
}
2020-03-02 22:54:57 +00:00
2020-04-10 15:42:34 +00:00
e . logf ( "wgengine: Reconfig done" )
2020-03-02 22:54:57 +00:00
return nil
2020-02-05 22:16:58 +00:00
}
2020-03-25 07:47:55 +00:00
func ( e * userspaceEngine ) GetFilter ( ) * filter . Filter {
2020-03-25 15:40:36 +00:00
e . mu . Lock ( )
defer e . mu . Unlock ( )
2020-03-25 07:47:55 +00:00
return e . filt
}
2020-02-05 22:16:58 +00:00
func ( e * userspaceEngine ) SetFilter ( filt * filter . Filter ) {
var filtin , filtout func ( b [ ] byte ) device . FilterResult
if filt == nil {
2020-04-11 15:35:34 +00:00
e . logf ( "wgengine: nil filter provided; no access restrictions." )
2020-02-05 22:16:58 +00:00
} else {
2020-02-14 23:03:25 +00:00
ft , ft_ok := e . tundev . ( * fakeTun )
2020-02-05 22:16:58 +00:00
filtin = func ( b [ ] byte ) device . FilterResult {
runf := filter . LogDrops
//runf |= filter.HexdumpDrops
runf |= filter . LogAccepts
//runf |= filter.HexdumpAccepts
q := & packet . QDecode { }
if filt . RunIn ( b , q , runf ) == filter . Accept {
// Only in fake mode, answer any incoming pings
if ft_ok && q . IsEchoRequest ( ) {
pb := q . EchoRespond ( )
ft . InsertRead ( pb )
// We already handled it, stop.
return device . FilterDrop
}
return device . FilterAccept
}
return device . FilterDrop
}
filtout = func ( b [ ] byte ) device . FilterResult {
runf := filter . LogDrops
//runf |= filter.HexdumpDrops
runf |= filter . LogAccepts
//runf |= filter.HexdumpAccepts
q := & packet . QDecode { }
if filt . RunOut ( b , q , runf ) == filter . Accept {
return device . FilterAccept
}
return device . FilterDrop
}
}
e . wgLock . Lock ( )
defer e . wgLock . Unlock ( )
e . wgdev . SetFilterInOut ( filtin , filtout )
2020-03-25 15:40:36 +00:00
e . mu . Lock ( )
e . filt = filt
e . mu . Unlock ( )
2020-02-05 22:16:58 +00:00
}
func ( e * userspaceEngine ) SetStatusCallback ( cb StatusCallback ) {
2020-02-28 17:32:06 +00:00
e . mu . Lock ( )
defer e . mu . Unlock ( )
2020-02-05 22:16:58 +00:00
e . statusCallback = cb
}
2020-02-28 17:32:06 +00:00
func ( e * userspaceEngine ) getStatusCallback ( ) StatusCallback {
e . mu . Lock ( )
defer e . mu . Unlock ( )
return e . statusCallback
}
2020-04-08 15:42:38 +00:00
// TODO: this function returns an error but it's always nil, and when
// there's actually a problem it just calls log.Fatal. Why?
2020-02-05 22:16:58 +00:00
func ( e * userspaceEngine ) getStatus ( ) ( * Status , error ) {
e . wgLock . Lock ( )
defer e . wgLock . Unlock ( )
if e . wgdev == nil {
// RequestStatus was invoked before the wgengine has
// finished initializing. This can happen when wgegine
// provides a callback to magicsock for endpoint
// updates that calls RequestStatus.
return nil , nil
}
2020-04-08 15:42:38 +00:00
// lineLen is the max UAPI line we expect. The longest I see is
// len("preshared_key=")+64 hex+"\n" == 79. Add some slop.
const lineLen = 100
pr , pw := io . Pipe ( )
errc := make ( chan error , 1 )
go func ( ) {
defer pw . Close ( )
bw := bufio . NewWriterSize ( pw , lineLen )
// TODO(apenwarr): get rid of silly uapi stuff for in-process comms
// FIXME: get notified of status changes instead of polling.
if err := e . wgdev . IpcGetOperation ( bw ) ; err != nil {
errc <- fmt . Errorf ( "IpcGetOperation: %w" , err )
return
}
errc <- bw . Flush ( )
} ( )
2020-02-05 22:16:58 +00:00
pp := make ( map [ wgcfg . Key ] * PeerStatus )
2020-04-08 15:42:38 +00:00
p := & PeerStatus { }
2020-02-05 22:16:58 +00:00
var hst1 , hst2 , n int64
var err error
2020-04-08 15:42:38 +00:00
bs := bufio . NewScanner ( pr )
bs . Buffer ( make ( [ ] byte , lineLen ) , lineLen )
for bs . Scan ( ) {
line := bs . Bytes ( )
k := line
var v mem . RO
if i := bytes . IndexByte ( line , '=' ) ; i != - 1 {
k = line [ : i ]
v = mem . B ( line [ i + 1 : ] )
2020-02-05 22:16:58 +00:00
}
2020-04-08 15:42:38 +00:00
switch string ( k ) {
2020-02-05 22:16:58 +00:00
case "public_key" :
2020-04-08 15:42:38 +00:00
pk , err := key . NewPublicFromHexMem ( v )
2020-02-05 22:16:58 +00:00
if err != nil {
2020-04-11 15:35:34 +00:00
log . Fatalf ( "IpcGetOperation: invalid key %#v" , v )
2020-02-05 22:16:58 +00:00
}
p = & PeerStatus { }
2020-04-08 15:42:38 +00:00
pp [ wgcfg . Key ( pk ) ] = p
2020-02-05 22:16:58 +00:00
2020-02-11 03:04:52 +00:00
key := tailcfg . NodeKey ( pk )
2020-02-05 22:16:58 +00:00
p . NodeKey = key
case "rx_bytes" :
2020-04-08 15:42:38 +00:00
n , err = v . ParseInt ( 10 , 64 )
2020-02-05 22:16:58 +00:00
p . RxBytes = ByteCount ( n )
if err != nil {
2020-04-11 15:35:34 +00:00
log . Fatalf ( "IpcGetOperation: rx_bytes invalid: %#v" , line )
2020-02-05 22:16:58 +00:00
}
case "tx_bytes" :
2020-04-08 15:42:38 +00:00
n , err = v . ParseInt ( 10 , 64 )
2020-02-05 22:16:58 +00:00
p . TxBytes = ByteCount ( n )
if err != nil {
2020-04-11 15:35:34 +00:00
log . Fatalf ( "IpcGetOperation: tx_bytes invalid: %#v" , line )
2020-02-05 22:16:58 +00:00
}
case "last_handshake_time_sec" :
2020-04-08 15:42:38 +00:00
hst1 , err = v . ParseInt ( 10 , 64 )
2020-02-05 22:16:58 +00:00
if err != nil {
2020-04-11 15:35:34 +00:00
log . Fatalf ( "IpcGetOperation: hst1 invalid: %#v" , line )
2020-02-05 22:16:58 +00:00
}
case "last_handshake_time_nsec" :
2020-04-08 15:42:38 +00:00
hst2 , err = v . ParseInt ( 10 , 64 )
2020-02-05 22:16:58 +00:00
if err != nil {
2020-04-11 15:35:34 +00:00
log . Fatalf ( "IpcGetOperation: hst2 invalid: %#v" , line )
2020-02-05 22:16:58 +00:00
}
if hst1 != 0 || hst2 != 0 {
p . LastHandshake = time . Unix ( hst1 , hst2 )
} // else leave at time.IsZero()
}
}
2020-04-08 15:42:38 +00:00
if err := bs . Err ( ) ; err != nil {
log . Fatalf ( "reading IpcGetOperation output: %v" , err )
}
if err := <- errc ; err != nil {
log . Fatalf ( "IpcGetOperation: %v" , err )
}
2020-02-05 22:16:58 +00:00
e . mu . Lock ( )
defer e . mu . Unlock ( )
var peers [ ] PeerStatus
for _ , pk := range e . peerSequence {
p := pp [ pk ]
if p == nil {
p = & PeerStatus { }
}
peers = append ( peers , * p )
}
if len ( pp ) != len ( e . peerSequence ) {
2020-04-11 15:35:34 +00:00
e . logf ( "wg status returned %v peers, expected %v" , len ( pp ) , len ( e . peerSequence ) )
2020-02-05 22:16:58 +00:00
}
return & Status {
LocalAddrs : append ( [ ] string ( nil ) , e . endpoints ... ) ,
Peers : peers ,
2020-03-19 06:55:14 +00:00
DERPs : e . magicConn . DERPs ( ) ,
2020-02-05 22:16:58 +00:00
} , nil
}
func ( e * userspaceEngine ) RequestStatus ( ) {
// This is slightly tricky. e.getStatus() can theoretically get
// blocked inside wireguard for a while, and RequestStatus() is
// sometimes called from a goroutine, so we don't want a lot of
// them hanging around. On the other hand, requesting multiple
// status updates simultaneously is pointless anyway; they will
// all say the same thing.
// Enqueue at most one request. If one is in progress already, this
// adds one more to the queue. If one has been requested but not
// started, it is a no-op.
select {
case e . reqCh <- struct { } { } :
default :
}
// Dequeue at most one request. Another thread may have already
// dequeued the request we enqueued above, which is fine, since the
// information is guaranteed to be at least as recent as the current
// call to RequestStatus().
select {
case <- e . reqCh :
s , err := e . getStatus ( )
if s == nil && err == nil {
2020-04-11 15:35:34 +00:00
e . logf ( "RequestStatus: weird: both s and err are nil" )
2020-02-05 22:16:58 +00:00
return
}
2020-02-28 17:32:06 +00:00
if cb := e . getStatusCallback ( ) ; cb != nil {
cb ( s , err )
2020-02-05 22:16:58 +00:00
}
default :
}
}
func ( e * userspaceEngine ) Close ( ) {
2020-02-25 16:06:29 +00:00
e . mu . Lock ( )
for key , cancel := range e . pingers {
delete ( e . pingers , key )
cancel ( )
}
e . mu . Unlock ( )
2020-02-20 17:47:33 +00:00
r := bufio . NewReader ( strings . NewReader ( "" ) )
e . wgdev . IpcSetOperation ( r )
2020-03-08 12:49:57 +00:00
e . wgdev . Close ( )
2020-02-17 17:00:38 +00:00
e . linkMon . Close ( )
2020-02-05 22:16:58 +00:00
e . router . Close ( )
e . magicConn . Close ( )
close ( e . waitCh )
}
func ( e * userspaceEngine ) Wait ( ) {
<- e . waitCh
}
2020-03-13 03:10:11 +00:00
func ( e * userspaceEngine ) setLinkState ( st * interfaces . State ) ( changed bool ) {
if st == nil {
return false
}
e . mu . Lock ( )
defer e . mu . Unlock ( )
changed = e . linkState == nil || ! st . Equal ( e . linkState )
e . linkState = st
return changed
}
2020-02-05 22:16:58 +00:00
func ( e * userspaceEngine ) LinkChange ( isExpensive bool ) {
2020-03-13 03:10:11 +00:00
cur , err := getLinkState ( )
if err != nil {
e . logf ( "LinkChange: interfaces.GetState: %v" , err )
return
}
2020-04-10 02:10:55 +00:00
cur . IsExpensive = isExpensive
2020-03-13 03:10:11 +00:00
needRebind := e . setLinkState ( cur )
e . logf ( "LinkChange(isExpensive=%v); needsRebind=%v" , isExpensive , needRebind )
why := "link-change-minor"
if needRebind {
why = "link-change-major"
e . magicConn . Rebind ( )
}
e . magicConn . ReSTUN ( why )
2020-02-05 22:16:58 +00:00
}
2020-03-04 06:21:56 +00:00
2020-03-13 03:10:11 +00:00
func getLinkState ( ) ( * interfaces . State , error ) {
s , err := interfaces . GetState ( )
if s != nil {
s . RemoveTailscaleInterfaces ( )
}
return s , err
}
2020-03-04 06:21:56 +00:00
func ( e * userspaceEngine ) SetNetInfoCallback ( cb NetInfoCallback ) {
e . magicConn . SetNetInfoCallback ( cb )
}
2020-03-04 20:21:40 +00:00
func ( e * userspaceEngine ) SetDERPEnabled ( v bool ) {
e . magicConn . SetDERPEnabled ( v )
}
2020-03-26 05:57:46 +00:00
func ( e * userspaceEngine ) UpdateStatus ( sb * ipnstate . StatusBuilder ) {
st , err := e . getStatus ( )
if err != nil {
e . logf ( "wgengine: getStatus: %v" , err )
return
}
for _ , ps := range st . Peers {
sb . AddPeer ( key . Public ( ps . NodeKey ) , & ipnstate . PeerStatus {
RxBytes : int64 ( ps . RxBytes ) ,
TxBytes : int64 ( ps . TxBytes ) ,
LastHandshake : ps . LastHandshake ,
InEngine : true ,
} )
}
e . magicConn . UpdateStatus ( sb )
}
2020-04-10 20:44:08 +00:00
// diagnoseTUNFailure is called if tun.CreateTUN fails, to poke around
// the system and log some diagnostic info that might help debug why
// TUN failed. Because TUN's already failed and things the program's
// about to end, we might as well log a lot.
func diagnoseTUNFailure ( logf logger . Logf ) {
switch runtime . GOOS {
case "linux" :
diagnoseLinuxTUNFailure ( logf )
default :
logf ( "no TUN failure diagnostics for OS %q" , runtime . GOOS )
}
}
func diagnoseLinuxTUNFailure ( logf logger . Logf ) {
kernel , err := exec . Command ( "uname" , "-r" ) . Output ( )
kernel = bytes . TrimSpace ( kernel )
if err != nil {
logf ( "no TUN, and failed to look up kernel version: %v" , err )
return
}
logf ( "Linux kernel version: %s" , kernel )
modprobeOut , err := exec . Command ( "/sbin/modprobe" , "tun" ) . CombinedOutput ( )
if err == nil {
logf ( "'modprobe tun' successful" )
// Either tun is currently loaded, or it's statically
// compiled into the kernel (which modprobe checks
// with /lib/modules/$(uname -r)/modules.builtin)
//
// So if there's a problem at this point, it's
// probably because /dev/net/tun doesn't exist.
const dev = "/dev/net/tun"
if fi , err := os . Stat ( dev ) ; err != nil {
logf ( "tun module loaded in kernel, but %s does not exist" , dev )
} else {
logf ( "%s: %v" , dev , fi . Mode ( ) )
}
// We failed to find why it failed. Just let our
// caller report the error it got from wireguard-go.
return
}
logf ( "is CONFIG_TUN enabled in your kernel? `modprobe tun` failed with: %s" , modprobeOut )
distro := linuxDistro ( )
switch distro {
case "debian" :
dpkgOut , err := exec . Command ( "dpkg" , "-S" , "kernel/drivers/net/tun.ko" ) . CombinedOutput ( )
if len ( bytes . TrimSpace ( dpkgOut ) ) == 0 || err != nil {
logf ( "tun module not loaded nor found on disk" )
return
}
if ! bytes . Contains ( dpkgOut , kernel ) {
logf ( "kernel/drivers/net/tun.ko found on disk, but not for current kernel; are you in middle of a system update and haven't rebooted? found: %s" , dpkgOut )
}
2020-04-13 16:22:08 +00:00
case "arch" :
findOut , err := exec . Command ( "find" , "/lib/modules/" , "-path" , "*/net/tun.ko*" ) . CombinedOutput ( )
if len ( bytes . TrimSpace ( findOut ) ) == 0 || err != nil {
logf ( "tun module not loaded nor found on disk" )
return
}
if ! bytes . Contains ( findOut , kernel ) {
logf ( "kernel/drivers/net/tun.ko found on disk, but not for current kernel; are you in middle of a system update and haven't rebooted? found: %s" , findOut )
}
2020-04-10 20:44:08 +00:00
}
}
func linuxDistro ( ) string {
if _ , err := os . Stat ( "/etc/debian_version" ) ; err == nil {
return "debian"
}
2020-04-13 16:22:08 +00:00
if _ , err := os . Stat ( "/etc/arch-release" ) ; err == nil {
return "arch"
}
2020-04-10 20:44:08 +00:00
return ""
}