| 
									
										
										
										
											2020-02-05 14:16:58 -08: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 ( | 
					
						
							|  |  |  | 	"log" | 
					
						
							|  |  |  | 	"runtime/pprof" | 
					
						
							| 
									
										
										
										
											2020-02-11 15:21:24 -08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/tailscale/wireguard-go/wgcfg" | 
					
						
							| 
									
										
										
										
											2020-03-25 22:57:46 -07:00
										 |  |  | 	"tailscale.com/ipn/ipnstate" | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 	"tailscale.com/wgengine/filter" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewWatchdog wraps an Engine and makes sure that all methods complete | 
					
						
							|  |  |  | // within a reasonable amount of time. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // If they do not, the watchdog crashes the process. | 
					
						
							|  |  |  | func NewWatchdog(e Engine) Engine { | 
					
						
							|  |  |  | 	return &watchdogEngine{ | 
					
						
							|  |  |  | 		wrap:    e, | 
					
						
							|  |  |  | 		logf:    log.Printf, | 
					
						
							|  |  |  | 		fatalf:  log.Fatalf, | 
					
						
							|  |  |  | 		maxWait: 45 * time.Second, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type watchdogEngine struct { | 
					
						
							|  |  |  | 	wrap    Engine | 
					
						
							|  |  |  | 	logf    func(format string, args ...interface{}) | 
					
						
							|  |  |  | 	fatalf  func(format string, args ...interface{}) | 
					
						
							|  |  |  | 	maxWait time.Duration | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *watchdogEngine) watchdogErr(name string, fn func() error) error { | 
					
						
							|  |  |  | 	errCh := make(chan error) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		errCh <- fn() | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	t := time.NewTimer(e.maxWait) | 
					
						
							|  |  |  | 	select { | 
					
						
							|  |  |  | 	case err := <-errCh: | 
					
						
							|  |  |  | 		t.Stop() | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	case <-t.C: | 
					
						
							| 
									
										
										
										
											2020-02-11 15:21:24 -08:00
										 |  |  | 		buf := new(strings.Builder) | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | 		pprof.Lookup("goroutine").WriteTo(buf, 1) | 
					
						
							|  |  |  | 		e.logf("wgengine watchdog stacks:\n%s", buf.String()) | 
					
						
							|  |  |  | 		e.fatalf("wgengine: watchdog timeout on %s", name) | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *watchdogEngine) watchdog(name string, fn func()) { | 
					
						
							|  |  |  | 	e.watchdogErr(name, func() error { | 
					
						
							|  |  |  | 		fn() | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *watchdogEngine) Reconfig(cfg *wgcfg.Config, dnsDomains []string) error { | 
					
						
							|  |  |  | 	return e.watchdogErr("Reconfig", func() error { return e.wrap.Reconfig(cfg, dnsDomains) }) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-03-25 03:47:55 -04:00
										 |  |  | func (e *watchdogEngine) GetFilter() *filter.Filter { | 
					
						
							|  |  |  | 	var x *filter.Filter | 
					
						
							|  |  |  | 	e.watchdog("GetFilter", func() { x = e.wrap.GetFilter() }) | 
					
						
							|  |  |  | 	return x | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | func (e *watchdogEngine) SetFilter(filt *filter.Filter) { | 
					
						
							|  |  |  | 	e.watchdog("SetFilter", func() { e.wrap.SetFilter(filt) }) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | func (e *watchdogEngine) SetStatusCallback(cb StatusCallback) { | 
					
						
							|  |  |  | 	e.watchdog("SetStatusCallback", func() { e.wrap.SetStatusCallback(cb) }) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-03-25 22:57:46 -07:00
										 |  |  | func (e *watchdogEngine) UpdateStatus(sb *ipnstate.StatusBuilder) { | 
					
						
							|  |  |  | 	e.watchdog("UpdateStatus", func() { e.wrap.UpdateStatus(sb) }) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-03-03 22:21:56 -08:00
										 |  |  | func (e *watchdogEngine) SetNetInfoCallback(cb NetInfoCallback) { | 
					
						
							|  |  |  | 	e.watchdog("SetNetInfoCallback", func() { e.wrap.SetNetInfoCallback(cb) }) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | func (e *watchdogEngine) RequestStatus() { | 
					
						
							|  |  |  | 	e.watchdog("RequestStatus", func() { e.wrap.RequestStatus() }) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | func (e *watchdogEngine) LinkChange(isExpensive bool) { | 
					
						
							|  |  |  | 	e.watchdog("LinkChange", func() { e.wrap.LinkChange(isExpensive) }) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-03-04 12:21:40 -08:00
										 |  |  | func (e *watchdogEngine) SetDERPEnabled(v bool) { | 
					
						
							|  |  |  | 	e.watchdog("SetDERPEnabled", func() { e.wrap.SetDERPEnabled(v) }) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-02-05 14:16:58 -08:00
										 |  |  | func (e *watchdogEngine) Close() { | 
					
						
							|  |  |  | 	e.watchdog("Close", e.wrap.Close) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | func (e *watchdogEngine) Wait() { | 
					
						
							|  |  |  | 	e.wrap.Wait() | 
					
						
							|  |  |  | } |