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-07-23 22:15:28 +00:00
"encoding/binary"
2020-05-17 16:51:38 +00:00
"errors"
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-07-31 20:27:09 +00:00
"net"
2020-04-10 20:44:08 +00:00
"os"
"os/exec"
"runtime"
2020-07-29 23:14:35 +00:00
"strconv"
2020-02-05 22:16:58 +00:00
"strings"
"sync"
2020-06-09 18:00:48 +00:00
"sync/atomic"
2020-02-05 22:16:58 +00:00
"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-07-07 19:25:32 +00:00
"inet.af/netaddr"
2020-06-25 18:04:52 +00:00
"tailscale.com/control/controlclient"
2020-06-28 17:58:21 +00:00
"tailscale.com/internal/deepprint"
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-11-10 00:16:04 +00:00
"tailscale.com/net/packet"
2020-07-31 20:27:09 +00:00
"tailscale.com/net/tsaddr"
2020-09-21 21:02:58 +00:00
"tailscale.com/net/tshttpproxy"
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-07-29 23:14:35 +00:00
"tailscale.com/version"
2020-09-11 02:55:09 +00:00
"tailscale.com/version/distro"
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-04-30 20:20:09 +00:00
"tailscale.com/wgengine/router"
2020-06-08 22:19:26 +00:00
"tailscale.com/wgengine/tsdns"
2020-05-13 13:16:17 +00:00
"tailscale.com/wgengine/tstun"
2020-02-05 22:16:58 +00:00
)
2020-09-25 20:13:13 +00:00
// minimalMTU is the MTU we set on tailscale's TUN
2020-04-14 23:36:17 +00:00
// 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.
const minimalMTU = 1280
2020-07-07 19:25:32 +00:00
const (
magicDNSIP = 0x64646464 // 100.100.100.100
magicDNSPort = 53
)
2020-07-23 22:15:28 +00:00
// Lazy wireguard-go configuration parameters.
const (
// lazyPeerIdleThreshold is the idle duration after
// which we remove a peer from the wireguard configuration.
// (This includes peers that have never been idle, which
// effectively have infinite idleness)
lazyPeerIdleThreshold = 5 * time . Minute
2020-07-31 19:40:49 +00:00
// packetSendTimeUpdateFrequency controls how often we record
// the time that we wrote a packet to an IP address.
packetSendTimeUpdateFrequency = 10 * time . Second
// packetSendRecheckWireguardThreshold controls how long we can go
// between packet sends to an IP before checking to see
// whether this IP address needs to be added back to the
// Wireguard peer oconfig.
packetSendRecheckWireguardThreshold = 1 * time . Minute
2020-07-23 22:15:28 +00:00
)
2020-02-05 22:16:58 +00:00
type userspaceEngine struct {
2020-07-31 20:27:09 +00:00
logf logger . Logf
reqCh chan struct { }
waitCh chan struct { } // chan is closed when first Close call completes; contrast with closing bool
2020-08-06 21:57:03 +00:00
timeNow func ( ) time . Time
2020-07-31 20:27:09 +00:00
tundev * tstun . TUN
wgdev * device . Device
router router . Router
resolver * tsdns . Resolver
magicConn * magicsock . Conn
linkMon * monitor . Mon
2020-06-09 18:00:48 +00:00
2020-08-06 21:57:03 +00:00
testMaybeReconfigHook func ( ) // for tests; if non-nil, fires if maybeReconfigWireguardLocked called
2020-06-09 18:00:48 +00:00
// localAddrs is the set of IP addresses assigned to the local
// tunnel interface. It's used to reflect local packets
// incorrectly sent to us.
2020-11-09 23:34:03 +00:00
localAddrs atomic . Value // of map[packet.IP4]bool
2020-02-05 22:16:58 +00:00
2020-07-23 22:15:28 +00:00
wgLock sync . Mutex // serializes all wgdev operations; see lock order comment below
lastCfgFull wgcfg . Config
lastRouterSig string // of router.Config
lastEngineSigFull string // of full wireguard config
lastEngineSigTrim string // of trimmed wireguard config
recvActivityAt map [ tailcfg . DiscoKey ] time . Time
2020-08-26 19:20:09 +00:00
trimmedDisco map [ tailcfg . DiscoKey ] bool // set of disco keys of peers currently excluded from wireguard config
2020-11-09 23:34:03 +00:00
sentActivityAt map [ packet . IP4 ] * int64 // value is atomic int64 of unixtime
destIPActivityFuncs map [ packet . IP4 ] func ( )
2020-02-05 22:16:58 +00:00
2020-08-28 04:25:17 +00:00
mu sync . Mutex // guards following; see lock order comment below
closing bool // Close was called (even if we're still closing)
statusCallback StatusCallback
linkChangeCallback func ( major bool , newState * interfaces . State )
peerSequence [ ] wgcfg . Key
endpoints [ ] string
pingers map [ wgcfg . Key ] * pinger // legacy pingers for pre-discovery peers
linkState * interfaces . State
2020-03-25 15:40:36 +00:00
2020-08-05 23:36:53 +00:00
// Lock ordering: magicsock.Conn.mu, wgLock, then mu.
2020-02-05 22:16:58 +00:00
}
2020-06-08 22:19:26 +00:00
// RouterGen is the signature for a function that creates a
// router.Router.
type RouterGen func ( logf logger . Logf , wgdev * device . Device , tundev tun . Device ) ( router . Router , error )
type EngineConfig struct {
// Logf is the logging function used by the engine.
Logf logger . Logf
// TUN is the tun device used by the engine.
TUN tun . Device
// RouterGen is the function used to instantiate the router.
RouterGen RouterGen
// ListenPort is the port on which the engine will listen.
ListenPort uint16
2020-07-31 20:27:09 +00:00
// Fake determines whether this engine is running in fake mode,
// which disables such features as DNS configuration and unrestricted ICMP Echo responses.
Fake bool
2020-06-08 22:19:26 +00:00
}
2020-02-14 23:03:25 +00:00
func NewFakeUserspaceEngine ( logf logger . Logf , listenPort uint16 ) ( Engine , error ) {
2020-09-25 20:13:13 +00:00
logf ( "Starting userspace wireguard engine (with fake TUN device)" )
2020-06-08 22:19:26 +00:00
conf := EngineConfig {
2020-07-31 20:27:09 +00:00
Logf : logf ,
TUN : tstun . NewFakeTUN ( ) ,
RouterGen : router . NewFake ,
ListenPort : listenPort ,
Fake : true ,
2020-06-08 22:19:26 +00:00
}
return NewUserspaceEngineAdvanced ( conf )
2020-02-05 22:16:58 +00:00
}
2020-04-30 20:20:09 +00:00
// NewUserspaceEngine creates the named tun device and returns a
// Tailscale Engine running on it.
2020-02-14 23:03:25 +00:00
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-05-13 13:16:17 +00:00
tun , err := tun . CreateTUN ( tunname , minimalMTU )
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-06-08 22:19:26 +00:00
conf := EngineConfig {
Logf : logf ,
TUN : tun ,
RouterGen : router . New ,
ListenPort : listenPort ,
}
e , err := NewUserspaceEngineAdvanced ( conf )
2020-02-05 22:16:58 +00:00
if err != nil {
return nil , err
}
return e , err
}
2020-06-08 22:19:26 +00:00
// NewUserspaceEngineAdvanced is like NewUserspaceEngine
// but provides control over all config fields.
func NewUserspaceEngineAdvanced ( conf EngineConfig ) ( Engine , error ) {
return newUserspaceEngineAdvanced ( conf )
2020-02-14 23:03:25 +00:00
}
2020-02-05 22:16:58 +00:00
2020-06-08 22:19:26 +00:00
func newUserspaceEngineAdvanced ( conf EngineConfig ) ( _ Engine , reterr error ) {
logf := conf . Logf
2020-08-19 19:39:25 +00:00
rconf := tsdns . ResolverConfig {
2020-08-24 21:27:21 +00:00
Logf : conf . Logf ,
Forward : true ,
2020-08-19 19:39:25 +00:00
}
2020-02-05 22:16:58 +00:00
e := & userspaceEngine {
2020-08-06 21:57:03 +00:00
timeNow : time . Now ,
2020-07-31 20:27:09 +00:00
logf : logf ,
reqCh : make ( chan struct { } , 1 ) ,
waitCh : make ( chan struct { } ) ,
tundev : tstun . WrapTUN ( logf , conf . TUN ) ,
2020-08-19 19:39:25 +00:00
resolver : tsdns . NewResolver ( rconf ) ,
2020-07-31 20:27:09 +00:00
pingers : make ( map [ wgcfg . Key ] * pinger ) ,
2020-02-05 22:16:58 +00:00
}
2020-11-09 23:34:03 +00:00
e . localAddrs . Store ( map [ packet . IP4 ] bool { } )
2020-03-13 03:10:11 +00:00
e . linkState , _ = getLinkState ( )
2020-08-12 19:48:34 +00:00
logf ( "link state: %+v" , e . linkState )
2020-02-05 22:16:58 +00:00
2020-06-08 22:19:26 +00:00
// Respond to all pings only in fake mode.
2020-07-31 20:27:09 +00:00
if conf . Fake {
2020-06-08 22:19:26 +00:00
e . tundev . PostFilterIn = echoRespondToAll
}
2020-06-09 18:00:48 +00:00
e . tundev . PreFilterOut = e . handleLocalPackets
2020-06-08 22:19:26 +00:00
2020-09-21 21:02:58 +00:00
mon , err := monitor . New ( logf , func ( ) {
e . LinkChange ( false )
tshttpproxy . InvalidateCache ( )
} )
2020-02-17 17:00:38 +00:00
if err != nil {
2020-06-08 22:19:26 +00:00
e . tundev . Close ( )
2020-02-17 17:00:38 +00:00
return nil , err
}
e . linkMon = mon
2020-02-05 22:16:58 +00:00
endpointsFn := func ( endpoints [ ] string ) {
2020-08-25 19:14:57 +00:00
e . mu . Lock ( )
e . endpoints = append ( e . endpoints [ : 0 ] , endpoints ... )
e . mu . Unlock ( )
2020-02-05 22:16:58 +00:00
e . RequestStatus ( )
}
magicsockOpts := magicsock . Options {
2020-07-23 22:15:28 +00:00
Logf : logf ,
Port : conf . ListenPort ,
EndpointsFunc : endpointsFn ,
2020-08-25 20:21:29 +00:00
DERPActiveFunc : e . RequestStatus ,
2020-07-23 22:15:28 +00:00
IdleFunc : e . tundev . IdleDuration ,
NoteRecvActivity : e . noteReceiveActivity ,
2020-02-05 22:16:58 +00:00
}
2020-05-17 16:51:38 +00:00
e . magicConn , err = magicsock . NewConn ( magicsockOpts )
2020-02-05 22:16:58 +00:00
if err != nil {
2020-06-08 22:19:26 +00:00
e . tundev . Close ( )
2020-02-05 22:16:58 +00:00
return nil , fmt . Errorf ( "wgengine: %v" , err )
}
2020-10-06 22:22:46 +00:00
e . magicConn . SetNetworkUp ( e . linkState . AnyInterfaceUp ( ) )
2020-02-05 22:16:58 +00:00
// flags==0 because logf is already nested in another logger.
// The outer one can display the preferred log prefixes, etc.
2020-09-23 22:27:30 +00:00
dlog := logger . StdLogger ( logf )
2020-02-05 22:16:58 +00:00
logger := device . Logger {
Debug : dlog ,
Info : dlog ,
Error : dlog ,
}
opts := & device . DeviceOptions {
2020-05-13 13:16:17 +00:00
Logger : & logger ,
2020-07-16 03:30:45 +00:00
HandshakeDone : func ( peerKey wgcfg . Key , peer * device . Peer , deviceAllowedIPs * device . AllowedIPs ) {
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-07-16 04:08:25 +00:00
if e . magicConn . PeerHasDiscoKey ( tailcfg . NodeKey ( peerKey ) ) {
e . logf ( "wireguard handshake complete for %v" , peerKey . ShortString ( ) )
// This is a modern peer with discovery support. No need to send pings.
return
}
e . logf ( "wireguard handshake complete for %v; sending legacy pings" , peerKey . ShortString ( ) )
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
2020-07-16 03:30:45 +00:00
allowedIPs := deviceAllowedIPs . EntriesForPeer ( peer )
2020-03-08 11:08:38 +00:00
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-05-10 22:34:16 +00:00
// wgdev takes ownership of tundev, will close it when closed.
2020-09-23 22:27:30 +00:00
e . logf ( "Creating wireguard device..." )
2020-02-14 23:03:25 +00:00
e . wgdev = device . NewDevice ( e . tundev , opts )
defer func ( ) {
if reterr != nil {
e . wgdev . Close ( )
}
} ( )
2020-05-15 07:06:30 +00:00
// Pass the underlying tun.(*NativeDevice) to the router:
// routers do not Read or Write, but do access native interfaces.
2020-09-23 22:27:30 +00:00
e . logf ( "Creating router..." )
2020-06-08 22:19:26 +00:00
e . router , err = conf . RouterGen ( logf , e . wgdev , e . tundev . Unwrap ( ) )
2020-02-14 23:03:25 +00:00
if err != nil {
2020-05-17 16:51:38 +00:00
e . magicConn . Close ( )
2020-02-14 23:03:25 +00:00
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
}
}
} ( )
2020-09-23 22:27:30 +00:00
e . logf ( "Bringing wireguard device up..." )
2020-02-05 22:16:58 +00:00
e . wgdev . Up ( )
2020-09-23 22:27:30 +00:00
e . logf ( "Bringing router up..." )
2020-02-05 22:16:58 +00:00
if err := e . router . Up ( ) ; err != nil {
2020-05-17 16:51:38 +00:00
e . magicConn . Close ( )
2020-02-05 22:16:58 +00:00
e . wgdev . Close ( )
return nil , err
}
2020-05-11 20:16:52 +00:00
// TODO(danderson): we should delete this. It's pointless to apply
// a no-op settings here.
2020-09-21 21:02:58 +00:00
// TODO(bradfitz): counter-point: it tests the router implementation early
// to see if any part of it might fail.
2020-09-23 22:27:30 +00:00
e . logf ( "Clearing router settings..." )
2020-05-12 07:08:52 +00:00
if err := e . router . Set ( nil ) ; err != nil {
2020-05-17 16:51:38 +00:00
e . magicConn . Close ( )
2020-02-05 22:16:58 +00:00
e . wgdev . Close ( )
return nil , err
}
2020-09-23 22:27:30 +00:00
e . logf ( "Starting link monitor..." )
2020-02-17 17:00:38 +00:00
e . linkMon . Start ( )
2020-09-23 22:27:30 +00:00
e . logf ( "Starting magicsock..." )
2020-05-17 16:51:38 +00:00
e . magicConn . Start ( )
2020-02-05 22:16:58 +00:00
2020-09-23 22:27:30 +00:00
e . logf ( "Starting resolver..." )
2020-07-07 19:25:32 +00:00
e . resolver . Start ( )
go e . pollResolver ( )
2020-09-23 22:27:30 +00:00
e . logf ( "Engine created." )
2020-02-05 22:16:58 +00:00
return e , nil
}
2020-06-08 22:19:26 +00:00
// echoRespondToAll is an inbound post-filter responding to all echo requests.
2020-11-10 07:49:09 +00:00
func echoRespondToAll ( p * packet . Parsed , t * tstun . TUN ) filter . Response {
2020-06-08 22:19:26 +00:00
if p . IsEchoRequest ( ) {
header := p . ICMPHeader ( )
header . ToResponse ( )
2020-10-06 02:41:16 +00:00
outp := packet . Generate ( & header , p . Payload ( ) )
t . InjectOutbound ( outp )
// We already responded to it, but it's not an error.
// Proceed with regular delivery. (Since this code is only
// used in fake mode, regular delivery just means throwing
// it away. If this ever gets run in non-fake mode, you'll
// get double responses to pings, which is an indicator you
// shouldn't be doing that I guess.)
return filter . Accept
2020-06-08 22:19:26 +00:00
}
return filter . Accept
}
2020-06-09 18:00:48 +00:00
// handleLocalPackets inspects packets coming from the local network
// stack, and intercepts any packets that should be handled by
// tailscaled directly. Other packets are allowed to proceed into the
// main ACL filter.
2020-11-10 07:49:09 +00:00
func ( e * userspaceEngine ) handleLocalPackets ( p * packet . Parsed , t * tstun . TUN ) filter . Response {
2020-07-31 20:27:09 +00:00
if verdict := e . handleDNS ( p , t ) ; verdict == filter . Drop {
// local DNS handled the packet.
return filter . Drop
2020-06-09 18:00:48 +00:00
}
2020-11-10 08:04:27 +00:00
if runtime . GOOS == "darwin" && e . isLocalAddr ( p . DstIP4 ) {
2020-06-09 18:00:48 +00:00
// macOS NetworkExtension directs packets destined to the
// tunnel's local IP address into the tunnel, instead of
// looping back within the kernel network stack. We have to
// notice that an outbound packet is actually destined for
// ourselves, and loop it back into macOS.
t . InjectInboundCopy ( p . Buffer ( ) )
return filter . Drop
}
return filter . Accept
}
2020-11-09 23:34:03 +00:00
func ( e * userspaceEngine ) isLocalAddr ( ip packet . IP4 ) bool {
localAddrs , ok := e . localAddrs . Load ( ) . ( map [ packet . IP4 ] bool )
2020-06-09 18:00:48 +00:00
if ! ok {
e . logf ( "[unexpected] e.localAddrs was nil, can't check for loopback packet" )
return false
}
return localAddrs [ ip ]
}
2020-06-08 22:19:26 +00:00
// handleDNS is an outbound pre-filter resolving Tailscale domains.
2020-11-10 07:49:09 +00:00
func ( e * userspaceEngine ) handleDNS ( p * packet . Parsed , t * tstun . TUN ) filter . Response {
2020-11-10 08:04:27 +00:00
if p . DstIP4 == magicDNSIP && p . DstPort == magicDNSPort && p . IPProto == packet . UDP {
2020-07-07 19:25:32 +00:00
request := tsdns . Packet {
2020-08-06 22:11:12 +00:00
Payload : append ( [ ] byte ( nil ) , p . Payload ( ) ... ) ,
2020-11-10 08:04:27 +00:00
Addr : netaddr . IPPort { IP : p . SrcIP4 . Netaddr ( ) , Port : p . SrcPort } ,
2020-07-07 19:25:32 +00:00
}
err := e . resolver . EnqueueRequest ( request )
2020-06-08 22:19:26 +00:00
if err != nil {
2020-07-07 19:25:32 +00:00
e . logf ( "tsdns: enqueue: %v" , err )
2020-06-08 22:19:26 +00:00
}
return filter . Drop
}
return filter . Accept
}
2020-07-07 19:25:32 +00:00
// pollResolver reads responses from the DNS resolver and injects them inbound.
func ( e * userspaceEngine ) pollResolver ( ) {
for {
resp , err := e . resolver . NextResponse ( )
if err == tsdns . ErrClosed {
return
}
if err != nil {
e . logf ( "tsdns: error: %v" , err )
continue
}
2020-11-09 23:34:03 +00:00
h := packet . UDP4Header {
IP4Header : packet . IP4Header {
SrcIP : packet . IP4 ( magicDNSIP ) ,
DstIP : packet . IP4FromNetaddr ( resp . Addr . IP ) ,
2020-07-07 19:25:32 +00:00
} ,
SrcPort : magicDNSPort ,
DstPort : resp . Addr . Port ,
}
hlen := h . Len ( )
// TODO(dmytro): avoid this allocation without importing tstun quirks into tsdns.
const offset = tstun . PacketStartOffset
buf := make ( [ ] byte , offset + hlen + len ( resp . Payload ) )
copy ( buf [ offset + hlen : ] , resp . Payload )
h . Marshal ( buf [ offset : ] )
e . tundev . InjectInboundDirect ( buf , offset )
}
}
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-07-16 04:08:25 +00:00
//
// These are only used with legacy peers (before 0.100.0) that don't
// have advertised discovery keys.
2020-05-29 05:38:26 +00:00
type pinger struct {
e * userspaceEngine
done chan struct { } // closed after shutdown (not the ctx.Done() chan)
cancel context . CancelFunc
}
2020-02-25 16:06:29 +00:00
2020-05-29 05:38:26 +00:00
// close cleans up pinger and removes it from the userspaceEngine.pingers map.
// It cannot be called while p.e.mu is held.
func ( p * pinger ) close ( ) {
p . cancel ( )
<- p . done
}
2020-02-25 16:06:29 +00:00
2020-11-09 23:34:03 +00:00
func ( p * pinger ) run ( ctx context . Context , peerKey wgcfg . Key , ips [ ] wgcfg . IP , srcIP packet . IP4 ) {
2020-05-29 05:38:26 +00:00
defer func ( ) {
p . e . mu . Lock ( )
if p . e . pingers [ peerKey ] == p {
delete ( p . e . pingers , peerKey )
}
p . e . mu . Unlock ( )
2020-02-25 16:06:29 +00:00
2020-05-29 05:38:26 +00:00
close ( p . done )
} ( )
2020-02-25 16:06:29 +00:00
2020-11-09 23:34:03 +00:00
header := packet . ICMP4Header {
IP4Header : packet . IP4Header {
2020-06-04 22:42:44 +00:00
SrcIP : srcIP ,
} ,
2020-11-09 23:34:03 +00:00
Type : packet . ICMP4EchoRequest ,
Code : packet . ICMP4NoCode ,
2020-06-04 22:42:44 +00:00
}
2020-02-25 16:06:29 +00:00
// 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-11-09 23:34:03 +00:00
var dstIPs [ ] packet . IP4
2020-03-08 11:08:38 +00:00
for _ , ip := range ips {
2020-11-09 23:34:03 +00:00
dstIPs = append ( dstIPs , packet . NewIP4 ( ip . IP ( ) ) )
2020-03-08 11:08:38 +00:00
}
2020-02-25 16:06:29 +00:00
payload := [ ] byte ( "magicsock_spray" ) // no meaning
2020-06-04 22:42:44 +00:00
header . IPID = 1
2020-02-28 11:30:46 +00:00
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 {
2020-06-04 22:42:44 +00:00
header . DstIP = dstIP
// InjectOutbound take ownership of the packet, so we allocate.
b := packet . Generate ( & header , payload )
2020-05-29 05:38:26 +00:00
p . e . tundev . InjectOutbound ( b )
2020-03-08 11:08:38 +00:00
}
2020-06-04 22:42:44 +00:00
header . IPID ++
2020-02-28 11:30:46 +00:00
}
2020-05-29 05:38:26 +00:00
}
// pinger sends ping packets for a few seconds.
//
// These generated packets are used to ensure we trigger the spray logic in
// the magicsock package for NAT traversal.
2020-07-16 04:08:25 +00:00
//
// This is only used with legacy peers (before 0.100.0) that don't
// have advertised discovery keys.
2020-05-29 05:38:26 +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-11-09 23:34:03 +00:00
var srcIP packet . IP4
2020-05-29 05:38:26 +00:00
e . wgLock . Lock ( )
2020-07-23 22:15:28 +00:00
if len ( e . lastCfgFull . Addresses ) > 0 {
2020-11-09 23:34:03 +00:00
srcIP = packet . NewIP4 ( e . lastCfgFull . Addresses [ 0 ] . IP . IP ( ) )
2020-05-29 05:38:26 +00:00
}
e . wgLock . Unlock ( )
if srcIP == 0 {
e . logf ( "generating initial ping traffic: no source IP" )
return
}
ctx , cancel := context . WithCancel ( context . Background ( ) )
p := & pinger {
e : e ,
done : make ( chan struct { } ) ,
cancel : cancel ,
}
e . mu . Lock ( )
if e . closing {
e . mu . Unlock ( )
return
}
oldPinger := e . pingers [ peerKey ]
e . pingers [ peerKey ] = p
e . mu . Unlock ( )
if oldPinger != nil {
oldPinger . close ( )
}
p . run ( ctx , peerKey , ips , srcIP )
2020-02-25 16:06:29 +00:00
}
2020-08-20 20:21:25 +00:00
var (
debugTrimWireguardEnv = os . Getenv ( "TS_DEBUG_TRIM_WIREGUARD" )
debugTrimWireguard , _ = strconv . ParseBool ( debugTrimWireguardEnv )
)
2020-07-29 23:14:35 +00:00
// forceFullWireguardConfig reports whether we should give wireguard
// our full network map, even for inactive peers
//
// TODO(bradfitz): remove this after our 1.0 launch; we don't want to
// enable wireguard config trimming quite yet because it just landed
// and we haven't got enough time testing it.
func forceFullWireguardConfig ( numPeers int ) bool {
// Did the user explicitly enable trimmming via the environment variable knob?
2020-08-20 20:21:25 +00:00
if debugTrimWireguardEnv != "" {
return ! debugTrimWireguard
2020-07-29 23:14:35 +00:00
}
2020-08-20 20:21:25 +00:00
if opt := controlclient . TrimWGConfig ( ) ; opt != "" {
return ! opt . EqualBool ( true )
}
2020-07-29 23:14:35 +00:00
// On iOS with large networks, it's critical, so turn on trimming.
// Otherwise we run out of memory from wireguard-go goroutine stacks+buffers.
// This will be the default later for all platforms and network sizes.
iOS := runtime . GOOS == "darwin" && version . IsMobile ( )
if iOS && numPeers > 50 {
return false
}
2020-08-21 03:13:39 +00:00
return false
2020-07-29 23:14:35 +00:00
}
2020-07-23 22:15:28 +00:00
// isTrimmablePeer reports whether p is a peer that we can trim out of the
// network map.
//
// We can only trim peers that both a) support discovery (because we
// know who they are when we receive their data and don't need to rely
// on wireguard-go figuring it out) and b) for implementation
// simplicity, have only one IP address (an IPv4 /32), which is the
// common case for most peers. Subnet router nodes will just always be
// created in the wireguard-go config.
2020-07-29 23:14:35 +00:00
func isTrimmablePeer ( p * wgcfg . Peer , numPeers int ) bool {
if forceFullWireguardConfig ( numPeers ) {
return false
}
2020-07-23 22:15:28 +00:00
if len ( p . AllowedIPs ) != 1 || len ( p . Endpoints ) != 1 {
return false
}
if ! strings . HasSuffix ( p . Endpoints [ 0 ] . Host , ".disco.tailscale" ) {
return false
}
aip := p . AllowedIPs [ 0 ]
// TODO: IPv6 support, once we support IPv6 within the tunnel. In that case,
// len(p.AllowedIPs) probably will be more than 1.
if aip . Mask != 32 || ! aip . IP . Is4 ( ) {
return false
}
return true
}
// noteReceiveActivity is called by magicsock when a packet has been received
// by the peer using discovery key dk. Magicsock calls this no more than
// every 10 seconds for a given peer.
func ( e * userspaceEngine ) noteReceiveActivity ( dk tailcfg . DiscoKey ) {
e . wgLock . Lock ( )
defer e . wgLock . Unlock ( )
2020-08-26 19:20:09 +00:00
if _ , ok := e . recvActivityAt [ dk ] ; ! ok {
2020-07-23 22:15:28 +00:00
// Not a trimmable peer we care about tracking. (See isTrimmablePeer)
2020-08-26 19:20:09 +00:00
if e . trimmedDisco [ dk ] {
e . logf ( "wgengine: [unexpected] noteReceiveActivity called on idle discokey %v that's not in recvActivityAt" , dk . ShortString ( ) )
}
2020-07-23 22:15:28 +00:00
return
}
2020-08-06 21:57:03 +00:00
now := e . timeNow ( )
2020-07-23 22:15:28 +00:00
e . recvActivityAt [ dk ] = now
// If the last activity time jumped a bunch (say, at least
// half the idle timeout) then see if we need to reprogram
// Wireguard. This could probably be just
// lazyPeerIdleThreshold without the divide by 2, but
// maybeReconfigWireguardLocked is cheap enough to call every
// couple minutes (just not on every packet).
2020-08-26 19:20:09 +00:00
if e . trimmedDisco [ dk ] {
e . logf ( "wgengine: idle peer %v now active, reconfiguring wireguard" , dk . ShortString ( ) )
2020-07-23 22:15:28 +00:00
e . maybeReconfigWireguardLocked ( )
}
}
// isActiveSince reports whether the peer identified by (dk, ip) has
// had a packet sent to or received from it since t.
//
// e.wgLock must be held.
func ( e * userspaceEngine ) isActiveSince ( dk tailcfg . DiscoKey , ip wgcfg . IP , t time . Time ) bool {
if e . recvActivityAt [ dk ] . After ( t ) {
return true
}
2020-11-09 23:34:03 +00:00
pip := packet . IP4 ( binary . BigEndian . Uint32 ( ip . Addr [ 12 : ] ) )
2020-07-23 22:15:28 +00:00
timePtr , ok := e . sentActivityAt [ pip ]
if ! ok {
return false
}
unixTime := atomic . LoadInt64 ( timePtr )
return unixTime >= t . Unix ( )
}
// discoKeyFromPeer returns the DiscoKey for a wireguard config's Peer.
//
// Invariant: isTrimmablePeer(p) == true, so it should have 1 endpoint with
// Host of form "<64-hex-digits>.disco.tailscale". If invariant is violated,
// we return the zero value.
func discoKeyFromPeer ( p * wgcfg . Peer ) tailcfg . DiscoKey {
host := p . Endpoints [ 0 ] . Host
if len ( host ) < 64 {
return tailcfg . DiscoKey { }
}
k , err := key . NewPublicFromHexMem ( mem . S ( host [ : 64 ] ) )
if err != nil {
return tailcfg . DiscoKey { }
}
return tailcfg . DiscoKey ( k )
}
// e.wgLock must be held.
func ( e * userspaceEngine ) maybeReconfigWireguardLocked ( ) error {
2020-08-06 21:57:03 +00:00
if hook := e . testMaybeReconfigHook ; hook != nil {
hook ( )
return nil
}
2020-07-23 22:15:28 +00:00
full := e . lastCfgFull
// Compute a minimal config to pass to wireguard-go
// based on the full config. Prune off all the peers
// and only add the active ones back.
min := full
min . Peers = nil
// We'll only keep a peer around if it's been active in
// the past 5 minutes. That's more than WireGuard's key
// rotation time anyway so it's no harm if we remove it
// later if it's been inactive.
2020-08-06 21:57:03 +00:00
activeCutoff := e . timeNow ( ) . Add ( - lazyPeerIdleThreshold )
2020-07-23 22:15:28 +00:00
// Not all peers can be trimmed from the network map (see
// isTrimmablePeer). For those are are trimmable, keep track
// of their DiscoKey and Tailscale IPs. These are the ones
// we'll need to install tracking hooks for to watch their
// send/receive activity.
trackDisco := make ( [ ] tailcfg . DiscoKey , 0 , len ( full . Peers ) )
trackIPs := make ( [ ] wgcfg . IP , 0 , len ( full . Peers ) )
2020-08-26 19:20:09 +00:00
trimmedDisco := map [ tailcfg . DiscoKey ] bool { } // TODO: don't re-alloc this map each time
2020-07-23 22:15:28 +00:00
for i := range full . Peers {
p := & full . Peers [ i ]
2020-07-29 23:14:35 +00:00
if ! isTrimmablePeer ( p , len ( full . Peers ) ) {
2020-07-23 22:15:28 +00:00
min . Peers = append ( min . Peers , * p )
continue
}
tsIP := p . AllowedIPs [ 0 ] . IP
dk := discoKeyFromPeer ( p )
trackDisco = append ( trackDisco , dk )
trackIPs = append ( trackIPs , tsIP )
if e . isActiveSince ( dk , tsIP , activeCutoff ) {
min . Peers = append ( min . Peers , * p )
2020-08-26 19:20:09 +00:00
} else {
trimmedDisco [ dk ] = true
2020-07-23 22:15:28 +00:00
}
}
2020-10-13 19:04:52 +00:00
if ! deepprint . UpdateHash ( & e . lastEngineSigTrim , min , trimmedDisco , trackDisco , trackIPs ) {
2020-07-23 22:15:28 +00:00
// No changes
return nil
}
2020-08-26 19:20:09 +00:00
e . trimmedDisco = trimmedDisco
2020-07-23 22:15:28 +00:00
e . updateActivityMapsLocked ( trackDisco , trackIPs )
e . logf ( "wgengine: Reconfig: configuring userspace wireguard config (with %d/%d peers)" , len ( min . Peers ) , len ( full . Peers ) )
if err := e . wgdev . Reconfig ( & min ) ; err != nil {
e . logf ( "wgdev.Reconfig: %v" , err )
return err
}
return nil
}
// updateActivityMapsLocked updates the data structures used for tracking the activity
// of wireguard peers that we might add/remove dynamically from the real config
// as given to wireguard-go.
//
// e.wgLock must be held.
func ( e * userspaceEngine ) updateActivityMapsLocked ( trackDisco [ ] tailcfg . DiscoKey , trackIPs [ ] wgcfg . IP ) {
// Generate the new map of which discokeys we want to track
// receive times for.
mr := map [ tailcfg . DiscoKey ] time . Time { } // TODO: only recreate this if set of keys changed
for _ , dk := range trackDisco {
// Preserve old times in the new map, but also
// populate map entries for new trackDisco values with
// time.Time{} zero values. (Only entries in this map
// are tracked, so the Time zero values allow it to be
// tracked later)
mr [ dk ] = e . recvActivityAt [ dk ]
}
e . recvActivityAt = mr
oldTime := e . sentActivityAt
2020-11-09 23:34:03 +00:00
e . sentActivityAt = make ( map [ packet . IP4 ] * int64 , len ( oldTime ) )
2020-07-23 22:15:28 +00:00
oldFunc := e . destIPActivityFuncs
2020-11-09 23:34:03 +00:00
e . destIPActivityFuncs = make ( map [ packet . IP4 ] func ( ) , len ( oldFunc ) )
2020-07-23 22:15:28 +00:00
for _ , wip := range trackIPs {
2020-11-09 23:34:03 +00:00
pip := packet . IP4 ( binary . BigEndian . Uint32 ( wip . Addr [ 12 : ] ) )
2020-07-23 22:15:28 +00:00
timePtr := oldTime [ pip ]
if timePtr == nil {
timePtr = new ( int64 )
}
e . sentActivityAt [ pip ] = timePtr
fn := oldFunc [ pip ]
if fn == nil {
// This is the func that gets run on every outgoing packet for tracked IPs:
fn = func ( ) {
2020-08-06 21:57:03 +00:00
now := e . timeNow ( ) . Unix ( )
2020-07-31 19:40:49 +00:00
old := atomic . LoadInt64 ( timePtr )
// How long's it been since we last sent a packet?
// For our first packet, old is Unix epoch time 0 (1970).
elapsedSec := now - old
if elapsedSec >= int64 ( packetSendTimeUpdateFrequency / time . Second ) {
atomic . StoreInt64 ( timePtr , now )
2020-07-23 22:15:28 +00:00
}
2020-07-31 19:40:49 +00:00
// On a big jump, assume we might no longer be in the wireguard
// config and go check.
if elapsedSec >= int64 ( packetSendRecheckWireguardThreshold / time . Second ) {
2020-07-23 22:15:28 +00:00
e . wgLock . Lock ( )
defer e . wgLock . Unlock ( )
e . maybeReconfigWireguardLocked ( )
}
}
}
e . destIPActivityFuncs [ pip ] = fn
}
e . tundev . SetDestIPActivityFuncs ( e . destIPActivityFuncs )
}
2020-05-12 07:08:52 +00:00
func ( e * userspaceEngine ) Reconfig ( cfg * wgcfg . Config , routerCfg * router . Config ) error {
2020-05-31 06:36:57 +00:00
if routerCfg == nil {
panic ( "routerCfg must not be nil" )
}
2020-11-09 23:34:03 +00:00
localAddrs := map [ packet . IP4 ] bool { }
2020-06-09 18:00:48 +00:00
for _ , addr := range routerCfg . LocalAddrs {
// TODO: ipv6
if ! addr . IP . Is4 ( ) {
continue
}
2020-11-09 23:34:03 +00:00
localAddrs [ packet . IP4FromNetaddr ( addr . IP ) ] = true
2020-06-09 18:00:48 +00:00
}
e . localAddrs . Store ( localAddrs )
2020-02-05 22:16:58 +00:00
e . wgLock . Lock ( )
defer e . wgLock . Unlock ( )
2020-04-18 15:48:01 +00:00
peerSet := make ( map [ key . Public ] struct { } , len ( cfg . Peers ) )
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-04-18 15:48:01 +00:00
peerSet [ key . Public ( p . PublicKey ) ] = struct { } { }
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-07-29 01:47:23 +00:00
engineChanged := deepprint . UpdateHash ( & e . lastEngineSigFull , cfg )
routerChanged := deepprint . UpdateHash ( & e . lastRouterSig , routerCfg )
2020-05-31 06:37:58 +00:00
if ! engineChanged && ! routerChanged {
2020-04-10 15:42:34 +00:00
return ErrNoChanges
2020-02-05 22:16:58 +00:00
}
2020-07-23 22:15:28 +00:00
e . lastCfgFull = cfg . Copy ( )
2020-04-10 15:42:34 +00:00
2020-07-23 22:15: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 {
e . logf ( "wgengine: Reconfig: SetPrivateKey: %v" , err )
}
e . magicConn . UpdatePeers ( peerSet )
2020-02-05 22:16:58 +00:00
2020-07-23 22:15:28 +00:00
if err := e . maybeReconfigWireguardLocked ( ) ; err != nil {
return err
2020-05-31 06:37:58 +00:00
}
2020-04-18 15:48:01 +00:00
2020-05-31 06:37:58 +00:00
if routerChanged {
2020-07-31 20:27:09 +00:00
if routerCfg . DNS . Proxied {
ips := routerCfg . DNS . Nameservers
2020-08-19 19:39:25 +00:00
upstreams := make ( [ ] net . Addr , len ( ips ) )
2020-07-31 20:27:09 +00:00
for i , ip := range ips {
2020-08-19 19:39:25 +00:00
stdIP := ip . IPAddr ( )
upstreams [ i ] = & net . UDPAddr {
IP : stdIP . IP ,
Port : 53 ,
Zone : stdIP . Zone ,
}
2020-07-31 20:27:09 +00:00
}
2020-08-19 19:39:25 +00:00
e . resolver . SetUpstreams ( upstreams )
2020-07-31 20:27:09 +00:00
routerCfg . DNS . Nameservers = [ ] netaddr . IP { tsaddr . TailscaleServiceIP ( ) }
}
2020-06-19 05:07:20 +00:00
e . logf ( "wgengine: Reconfig: configuring router" )
2020-05-31 06:37:58 +00:00
if err := e . router . Set ( routerCfg ) ; 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-05-13 13:16:17 +00:00
return e . tundev . GetFilter ( )
2020-03-25 07:47:55 +00:00
}
2020-02-05 22:16:58 +00:00
func ( e * userspaceEngine ) SetFilter ( filt * filter . Filter ) {
2020-05-13 13:16:17 +00:00
e . tundev . SetFilter ( filt )
2020-02-05 22:16:58 +00:00
}
2020-06-08 22:19:26 +00:00
func ( e * userspaceEngine ) SetDNSMap ( dm * tsdns . Map ) {
e . resolver . SetMap ( dm )
}
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 ) {
2020-08-05 23:36:53 +00:00
// Grab derpConns before acquiring wgLock to not violate lock ordering;
// the DERPs method acquires magicsock.Conn.mu.
// (See comment in userspaceEngine's declaration.)
derpConns := e . magicConn . DERPs ( )
2020-02-05 22:16:58 +00:00
e . wgLock . Lock ( )
defer e . wgLock . Unlock ( )
2020-05-17 16:51:38 +00:00
e . mu . Lock ( )
closing := e . closing
e . mu . Unlock ( )
if closing {
return nil , errors . New ( "engine closing; no status" )
}
2020-02-05 22:16:58 +00:00
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.
2020-07-16 03:30:45 +00:00
filter := device . IPCGetFilter {
// The allowed_ips are somewhat expensive to compute and they're
// unused below; request that they not be sent instead.
FilterAllowedIPs : true ,
}
if err := e . wgdev . IpcGetOperationFiltered ( bw , filter ) ; err != nil {
2020-04-08 15:42:38 +00:00
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-06-01 03:22:46 +00:00
n , err = mem . ParseInt ( v , 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-06-01 03:22:46 +00:00
n , err = mem . ParseInt ( v , 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-06-01 03:22:46 +00:00
hst1 , err = mem . ParseInt ( v , 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-06-01 03:22:46 +00:00
hst2 , err = mem . ParseInt ( v , 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 {
2020-07-23 22:15:28 +00:00
if p , ok := pp [ pk ] ; ok { // ignore idle ones not in wireguard-go's config
peers = append ( peers , * p )
2020-02-05 22:16:58 +00:00
}
}
return & Status {
LocalAddrs : append ( [ ] string ( nil ) , e . endpoints ... ) ,
Peers : peers ,
2020-08-05 23:36:53 +00:00
DERPs : derpConns ,
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-05-29 05:38:26 +00:00
var pingers [ ] * pinger
2020-02-25 16:06:29 +00:00
e . mu . Lock ( )
2020-05-17 16:51:38 +00:00
if e . closing {
e . mu . Unlock ( )
return
}
e . closing = true
2020-05-29 05:38:26 +00:00
for _ , pinger := range e . pingers {
pingers = append ( pingers , pinger )
2020-02-25 16:06:29 +00:00
}
e . mu . Unlock ( )
2020-02-20 17:47:33 +00:00
r := bufio . NewReader ( strings . NewReader ( "" ) )
e . wgdev . IpcSetOperation ( r )
2020-07-07 19:25:32 +00:00
e . resolver . Close ( )
2020-07-03 07:00:04 +00:00
e . magicConn . Close ( )
2020-02-17 17:00:38 +00:00
e . linkMon . Close ( )
2020-02-05 22:16:58 +00:00
e . router . Close ( )
2020-07-03 07:00:04 +00:00
e . wgdev . Close ( )
2020-09-18 15:04:15 +00:00
e . tundev . Close ( )
2020-05-29 05:38:26 +00:00
// Shut down pingers after tundev is closed (by e.wgdev.Close) so the
// synchronous close does not get stuck on InjectOutbound.
for _ , pinger := range pingers {
pinger . close ( )
}
2020-02-05 22:16:58 +00:00
close ( e . waitCh )
}
func ( e * userspaceEngine ) Wait ( ) {
<- e . waitCh
}
2020-08-28 04:25:17 +00:00
func ( e * userspaceEngine ) setLinkState ( st * interfaces . State ) ( changed bool , cb func ( major bool , newState * interfaces . State ) ) {
2020-03-13 03:10:11 +00:00
if st == nil {
2020-08-28 04:25:17 +00:00
return false , nil
2020-03-13 03:10:11 +00:00
}
e . mu . Lock ( )
defer e . mu . Unlock ( )
changed = e . linkState == nil || ! st . Equal ( e . linkState )
e . linkState = st
2020-08-28 04:25:17 +00:00
return changed , e . linkChangeCallback
2020-03-13 03:10:11 +00:00
}
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-08-28 04:25:17 +00:00
needRebind , linkChangeCallback := e . setLinkState ( cur )
2020-03-13 03:10:11 +00:00
2020-10-06 22:22:46 +00:00
up := cur . AnyInterfaceUp ( )
if ! up {
e . logf ( "LinkChange: all links down; pausing: %v" , cur )
} else if needRebind {
e . logf ( "LinkChange: major, rebinding. New state: %v" , cur )
2020-08-12 19:48:34 +00:00
} else {
e . logf ( "LinkChange: minor" )
}
2020-03-13 03:10:11 +00:00
2020-10-06 22:22:46 +00:00
e . magicConn . SetNetworkUp ( up )
2020-03-13 03:10:11 +00:00
why := "link-change-minor"
if needRebind {
why = "link-change-major"
e . magicConn . Rebind ( )
}
e . magicConn . ReSTUN ( why )
2020-08-28 04:25:17 +00:00
if linkChangeCallback != nil {
go linkChangeCallback ( needRebind , cur )
}
}
func ( e * userspaceEngine ) SetLinkChangeCallback ( cb func ( major bool , newState * interfaces . State ) ) {
e . mu . Lock ( )
defer e . mu . Unlock ( )
e . linkChangeCallback = cb
2020-10-05 22:12:35 +00:00
if e . linkState != nil {
go cb ( false , e . linkState )
}
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
2020-05-17 16:51:38 +00:00
func ( e * userspaceEngine ) SetDERPMap ( dm * tailcfg . DERPMap ) {
e . magicConn . SetDERPMap ( dm )
2020-03-04 20:21:40 +00:00
}
2020-03-26 05:57:46 +00:00
2020-06-25 18:04:52 +00:00
func ( e * userspaceEngine ) SetNetworkMap ( nm * controlclient . NetworkMap ) {
e . magicConn . SetNetworkMap ( nm )
}
2020-07-06 19:10:39 +00:00
func ( e * userspaceEngine ) DiscoPublicKey ( ) tailcfg . DiscoKey {
return e . magicConn . DiscoPublicKey ( )
2020-06-19 19:06:49 +00:00
}
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
2020-08-09 21:49:42 +00:00
func ( e * userspaceEngine ) Ping ( ip netaddr . IP , cb func ( * ipnstate . PingResult ) ) {
e . magicConn . Ping ( ip , cb )
}
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 )
2020-09-11 02:55:09 +00:00
switch distro . Get ( ) {
case distro . Debian :
2020-04-10 20:44:08 +00:00
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-09-11 02:55:09 +00:00
case distro . Arch :
2020-04-13 16:22:08 +00:00
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-09-22 17:28:40 +00:00
case distro . OpenWrt :
out , err := exec . Command ( "opkg" , "list-installed" ) . CombinedOutput ( )
if err != nil {
logf ( "error querying OpenWrt installed packages: %s" , out )
return
}
for _ , pkg := range [ ] string { "kmod-tun" , "ca-bundle" } {
if ! bytes . Contains ( out , [ ] byte ( pkg + " - " ) ) {
logf ( "Missing required package %s; run: opkg install %s" , pkg , pkg )
}
}
2020-04-10 20:44:08 +00:00
}
}