summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorClaire Wang <claire@tailscale.com>2023-08-23 10:40:42 -0400
committerClaire Wang <claire@tailscale.com>2023-08-23 10:40:42 -0400
commit7b419461eb2c99b86b52e611221028e58af2203b (patch)
tree717732c44a96aac5cb61ee38ef4758c6dba77b50
parent000c0a70f676814496d6adecf54cfcd23fe0c121 (diff)
downloadtailscale-clairew/tstime-net.tar.xz
tailscale-clairew/tstime-net.zip
net: use tstimeclairew/tstime-net
Updates #8587 Signed-off-by: Claire Wang <claire@tailscale.com>
-rw-r--r--net/connstats/stats.go15
-rw-r--r--net/dns/direct.go7
-rw-r--r--net/dns/manager.go4
-rw-r--r--net/dns/manager_windows.go8
-rw-r--r--net/dns/recursive/recursive.go11
-rw-r--r--net/dns/recursive/recursive_test.go4
-rw-r--r--net/dns/resolver/debug.go4
-rw-r--r--net/dns/resolver/forwarder.go9
-rw-r--r--net/dnscache/dnscache.go13
-rw-r--r--net/dnscache/messagecache.go9
-rw-r--r--net/dnscache/messagecache_test.go2
-rw-r--r--net/memnet/pipe.go11
-rw-r--r--net/netcheck/netcheck.go24
-rw-r--r--net/netcheck/netcheck_test.go4
-rw-r--r--net/netmon/netmon.go21
-rw-r--r--net/netmon/netmon_test.go2
-rw-r--r--net/netmon/netmon_windows.go8
-rw-r--r--net/netmon/polling.go7
-rw-r--r--net/ping/ping.go17
-rw-r--r--net/ping/ping_test.go2
-rw-r--r--net/portmapper/pcp.go2
-rw-r--r--net/portmapper/portmapper.go28
-rw-r--r--net/portmapper/upnp.go5
-rw-r--r--net/proxymux/mux.go6
-rw-r--r--net/sockstats/sockstats_tsgo.go14
-rw-r--r--net/tlsdial/tlsdial.go6
-rw-r--r--net/tshttpproxy/tshttpproxy.go7
-rw-r--r--net/tshttpproxy/tshttpproxy_windows.go4
-rw-r--r--net/tstun/ifstatus_windows.go12
-rw-r--r--net/tstun/tun.go3
-rw-r--r--net/tstun/wrap.go13
-rw-r--r--net/tstun/wrap_test.go4
32 files changed, 170 insertions, 116 deletions
diff --git a/net/connstats/stats.go b/net/connstats/stats.go
index dbcd946b8..092710d56 100644
--- a/net/connstats/stats.go
+++ b/net/connstats/stats.go
@@ -14,6 +14,7 @@ import (
"golang.org/x/sync/errgroup"
"tailscale.com/net/packet"
"tailscale.com/net/tsaddr"
+ "tailscale.com/tstime"
"tailscale.com/types/netlogtype"
)
@@ -30,6 +31,7 @@ type Statistics struct {
shutdownCtx context.Context
shutdown context.CancelFunc
group errgroup.Group
+ clock tstime.Clock
}
type connCnts struct {
@@ -45,7 +47,7 @@ type connCnts struct {
// The dump function is called from a single goroutine.
// Shutdown must be called to cleanup resources.
func NewStatistics(maxPeriod time.Duration, maxConns int, dump func(start, end time.Time, virtual, physical map[netlogtype.Connection]netlogtype.Counts)) *Statistics {
- s := &Statistics{maxConns: maxConns}
+ s := &Statistics{maxConns: maxConns, clock: tstime.StdClock{}}
s.connCntsCh = make(chan connCnts, 256)
s.shutdownCtx, s.shutdown = context.WithCancel(context.Background())
s.group.Go(func() error {
@@ -53,9 +55,10 @@ func NewStatistics(maxPeriod time.Duration, maxConns int, dump func(start, end t
// where waking up a process every maxPeriod when there is no activity
// is a drain on battery life. Switch this instead to instead use
// a time.Timer that is triggered upon network activity.
- ticker := new(time.Ticker)
+ var ticker tstime.TickerController
+ var tickerChannel <-chan time.Time
if maxPeriod > 0 {
- ticker = time.NewTicker(maxPeriod)
+ ticker, tickerChannel = s.clock.NewTicker(maxPeriod)
defer ticker.Stop()
}
@@ -63,7 +66,7 @@ func NewStatistics(maxPeriod time.Duration, maxConns int, dump func(start, end t
var cc connCnts
select {
case cc = <-s.connCntsCh:
- case <-ticker.C:
+ case <-tickerChannel:
cc = s.extract()
case <-s.shutdownCtx.Done():
cc = s.extract()
@@ -182,7 +185,7 @@ func (s *Statistics) preInsertConn() bool {
// Initialize the maps if nil.
if s.virtual == nil && s.physical == nil {
- s.start = time.Now().UTC()
+ s.start = s.clock.Now().UTC()
s.virtual = make(map[netlogtype.Connection]netlogtype.Counts)
s.physical = make(map[netlogtype.Connection]netlogtype.Counts)
}
@@ -200,7 +203,7 @@ func (s *Statistics) extractLocked() connCnts {
if len(s.virtual)+len(s.physical) == 0 {
return connCnts{}
}
- s.end = time.Now().UTC()
+ s.end = s.clock.Now().UTC()
cc := s.connCnts
s.connCnts = connCnts{}
return cc
diff --git a/net/dns/direct.go b/net/dns/direct.go
index e9279d13a..95e1174c9 100644
--- a/net/dns/direct.go
+++ b/net/dns/direct.go
@@ -22,11 +22,14 @@ import (
"tailscale.com/health"
"tailscale.com/net/dns/resolvconffile"
+ "tailscale.com/tstime"
"tailscale.com/types/logger"
"tailscale.com/util/dnsname"
"tailscale.com/version/distro"
)
+var clock = tstime.StdClock{}
+
// writeResolvConf writes DNS configuration in resolv.conf format to the given writer.
func writeResolvConf(w io.Writer, servers []netip.Addr, domains []dnsname.FQDN) error {
c := &resolvconffile.Config{
@@ -387,9 +390,9 @@ func (m *directManager) SetDNS(config OSConfig) (err error) {
// cause a disruptive DNS outage each time we reset an empty
// OS configuration.
if changed && isResolvedRunning() && !runningAsGUIDesktopUser() {
- t0 := time.Now()
+ t0 := clock.Now()
err := restartResolved()
- d := time.Since(t0).Round(time.Millisecond)
+ d := clock.Since(t0).Round(time.Millisecond)
if err != nil {
m.logf("error restarting resolved after %v: %v", d, err)
} else {
diff --git a/net/dns/manager.go b/net/dns/manager.go
index f177b5777..8d5cb38d2 100644
--- a/net/dns/manager.go
+++ b/net/dns/manager.go
@@ -355,7 +355,7 @@ func (s *dnsTCPSession) handleWrites() {
return // connection closed or timeout, teardown time
case resp := <-s.responses:
- s.conn.SetWriteDeadline(time.Now().Add(idleTimeoutTCP))
+ s.conn.SetWriteDeadline(clock.Now().Add(idleTimeoutTCP))
if err := binary.Write(s.conn, binary.BigEndian, uint16(len(resp))); err != nil {
s.m.logf("tcp write (len): %v", err)
return
@@ -392,7 +392,7 @@ func (s *dnsTCPSession) handleReads() {
return
default:
- s.conn.SetReadDeadline(time.Now().Add(idleTimeoutTCP))
+ s.conn.SetReadDeadline(clock.Now().Add(idleTimeoutTCP))
var reqLen uint16
if err := binary.Read(s.conn, binary.BigEndian, &reqLen); err != nil {
if err == io.EOF || err == io.ErrClosedPipe {
diff --git a/net/dns/manager_windows.go b/net/dns/manager_windows.go
index 4c05b7718..ef27b9615 100644
--- a/net/dns/manager_windows.go
+++ b/net/dns/manager_windows.go
@@ -342,24 +342,24 @@ func (m *windowsManager) SetDNS(cfg OSConfig) error {
// any cached split-horizon queries that are no longer the correct
// answer.
go func() {
- t0 := time.Now()
+ t0 := clock.Now()
m.logf("running ipconfig /registerdns ...")
cmd := exec.Command("ipconfig", "/registerdns")
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
err := cmd.Run()
- d := time.Since(t0).Round(time.Millisecond)
+ d := clock.Since(t0).Round(time.Millisecond)
if err != nil {
m.logf("error running ipconfig /registerdns after %v: %v", d, err)
} else {
m.logf("ran ipconfig /registerdns in %v", d)
}
- t0 = time.Now()
+ t0 = clock.Now()
m.logf("running ipconfig /flushdns ...")
cmd = exec.Command("ipconfig", "/flushdns")
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
err = cmd.Run()
- d = time.Since(t0).Round(time.Millisecond)
+ d = clock.Since(t0).Round(time.Millisecond)
if err != nil {
m.logf("error running ipconfig /flushdns after %v: %v", d, err)
} else {
diff --git a/net/dns/recursive/recursive.go b/net/dns/recursive/recursive.go
index 5b585483c..023638319 100644
--- a/net/dns/recursive/recursive.go
+++ b/net/dns/recursive/recursive.go
@@ -17,6 +17,7 @@ import (
"github.com/miekg/dns"
"tailscale.com/envknob"
"tailscale.com/net/netns"
+ "tailscale.com/tstime"
"tailscale.com/types/logger"
"tailscale.com/util/dnsname"
"tailscale.com/util/mak"
@@ -96,6 +97,8 @@ var rootServersV6 = []netip.Addr{
var debug = envknob.RegisterBool("TS_DEBUG_RECURSIVE_DNS")
+var clock = tstime.StdClock{}
+
// Resolver is a recursive DNS resolver that is designed for looking up A and AAAA records.
type Resolver struct {
// Dialer is used to create outbound connections. If nil, a zero
@@ -114,7 +117,7 @@ type Resolver struct {
testQueryHook func(name dnsname.FQDN, nameserver netip.Addr, protocol string, qtype dns.Type) (*dns.Msg, error)
testExchangeHook func(nameserver netip.Addr, network string, msg *dns.Msg) (*dns.Msg, error)
rootServers []netip.Addr
- timeNow func() time.Time
+ clock tstime.Clock
// Caching
// NOTE(andrew): if we make resolution parallel, this needs a mutex
@@ -150,10 +153,10 @@ type dnsMsgWithExpiry struct {
}
func (r *Resolver) now() time.Time {
- if r.timeNow != nil {
- return r.timeNow()
+ if r.clock != nil {
+ return r.clock.Now()
}
- return time.Now()
+ return clock.Now()
}
func (r *Resolver) logf(format string, args ...any) {
diff --git a/net/dns/recursive/recursive_test.go b/net/dns/recursive/recursive_test.go
index 0bfba383a..136e0aad7 100644
--- a/net/dns/recursive/recursive_test.go
+++ b/net/dns/recursive/recursive_test.go
@@ -41,8 +41,8 @@ func newResolver(tb testing.TB) *Resolver {
Step: 50 * time.Millisecond,
})
return &Resolver{
- Logf: tb.Logf,
- timeNow: clock.Now,
+ Logf: tb.Logf,
+ clock: clock,
}
}
diff --git a/net/dns/resolver/debug.go b/net/dns/resolver/debug.go
index f334af5c9..55c7cc317 100644
--- a/net/dns/resolver/debug.go
+++ b/net/dns/resolver/debug.go
@@ -54,7 +54,7 @@ func (fl *fwdLog) addName(name string) {
if len(fl.ent) == 0 {
return
}
- fl.ent[fl.pos] = fwdLogEntry{Domain: name, Time: time.Now()}
+ fl.ent[fl.pos] = fwdLogEntry{Domain: name, Time: clock.Now()}
fl.pos++
if fl.pos == len(fl.ent) {
fl.pos = 0
@@ -66,7 +66,7 @@ func (fl *fwdLog) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer fl.mu.Unlock()
fmt.Fprintf(w, "<html><h1>DNS forwards</h1>")
- now := time.Now()
+ now := clock.Now()
for i := 0; i < len(fl.ent); i++ {
ent := fl.ent[(i+fl.pos)%len(fl.ent)]
if ent.Domain == "" {
diff --git a/net/dns/resolver/forwarder.go b/net/dns/resolver/forwarder.go
index 85670e1d6..ce157db74 100644
--- a/net/dns/resolver/forwarder.go
+++ b/net/dns/resolver/forwarder.go
@@ -29,6 +29,7 @@ import (
"tailscale.com/net/netns"
"tailscale.com/net/sockstats"
"tailscale.com/net/tsdial"
+ "tailscale.com/tstime"
"tailscale.com/types/dnstype"
"tailscale.com/types/logger"
"tailscale.com/types/nettype"
@@ -202,8 +203,10 @@ type forwarder struct {
cloudHostFallback []resolverAndDelay
}
+var clock = tstime.StdClock{}
+
func init() {
- rand.Seed(time.Now().UnixNano())
+ rand.Seed(clock.Now().UnixNano())
}
func newForwarder(logf logger.Logf, netMon *netmon.Monitor, linkSel ForwardLinkSelector, dialer *tsdial.Dialer) *forwarder {
@@ -695,9 +698,9 @@ func (f *forwarder) forwardWithDestChan(ctx context.Context, query packet, respo
for i := range resolvers {
go func(rr *resolverAndDelay) {
if rr.startDelay > 0 {
- timer := time.NewTimer(rr.startDelay)
+ timer, timerChannel := clock.NewTimer(rr.startDelay)
select {
- case <-timer.C:
+ case <-timerChannel:
case <-ctx.Done():
timer.Stop()
return
diff --git a/net/dnscache/dnscache.go b/net/dnscache/dnscache.go
index f3fd50fb9..b23878903 100644
--- a/net/dnscache/dnscache.go
+++ b/net/dnscache/dnscache.go
@@ -22,6 +22,7 @@ import (
"tailscale.com/envknob"
"tailscale.com/net/netmon"
+ "tailscale.com/tstime"
"tailscale.com/types/logger"
"tailscale.com/util/cloudenv"
"tailscale.com/util/singleflight"
@@ -34,6 +35,8 @@ var single = &Resolver{
Forward: &net.Resolver{PreferGo: preferGoResolver()},
}
+var clock = tstime.StdClock{}
+
func preferGoResolver() bool {
// There does not appear to be a local resolver running
// on iOS, and NetworkExtension is good at isolating DNS.
@@ -251,7 +254,7 @@ func (r *Resolver) LookupIP(ctx context.Context, host string) (ip, v6 netip.Addr
func (r *Resolver) lookupIPCache(host string) (ip, ip6 netip.Addr, allIPs []netip.Addr, ok bool) {
r.mu.Lock()
defer r.mu.Unlock()
- if ent, ok := r.ipCache[host]; ok && ent.expires.After(time.Now()) {
+ if ent, ok := r.ipCache[host]; ok && ent.expires.After(clock.Now()) {
return ent.ip, ent.ip6, ent.allIPs, true
}
return zaddr, zaddr, nil, false
@@ -359,7 +362,7 @@ func (r *Resolver) addIPCache(host string, ip, ip6 netip.Addr, allIPs []netip.Ad
ip: ip,
ip6: ip6,
allIPs: allIPs,
- expires: time.Now().Add(d),
+ expires: clock.Now().Add(d),
}
}
@@ -510,7 +513,7 @@ func (dc *dialCall) noteDialResult(ip netip.Addr, err error) {
d := dc.d
d.mu.Lock()
defer d.mu.Unlock()
- d.pastConnect[ip] = time.Now()
+ d.pastConnect[ip] = clock.Now()
return
}
dc.mu.Lock()
@@ -585,9 +588,9 @@ func (dc *dialCall) raceDial(ctx context.Context, ips []netip.Addr) (net.Conn, e
go func() {
for i, ip := range ips {
if i != 0 {
- timer := time.NewTimer(fallbackDelay)
+ timer, timerChannel := clock.NewTimer(fallbackDelay)
select {
- case <-timer.C:
+ case <-timerChannel:
case <-failBoost:
timer.Stop()
case <-ctx.Done():
diff --git a/net/dnscache/messagecache.go b/net/dnscache/messagecache.go
index ebbf20faa..c3359221d 100644
--- a/net/dnscache/messagecache.go
+++ b/net/dnscache/messagecache.go
@@ -13,6 +13,7 @@ import (
"github.com/golang/groupcache/lru"
"golang.org/x/net/dns/dnsmessage"
+ "tailscale.com/tstime"
"tailscale.com/util/cmpx"
)
@@ -26,8 +27,8 @@ import (
// It's safe for concurrent use.
type MessageCache struct {
// Clock is a clock, for testing.
- // If nil, time.Now is used.
- Clock func() time.Time
+ // If nil, clock.Now is used.
+ Clock tstime.Clock
mu sync.Mutex
cacheSizeSet int // 0 means default
@@ -36,9 +37,9 @@ type MessageCache struct {
func (c *MessageCache) now() time.Time {
if c.Clock != nil {
- return c.Clock()
+ return c.Clock.Now()
}
- return time.Now()
+ return clock.Now()
}
// SetMaxCacheSize sets the maximum number of DNS cache entries that
diff --git a/net/dnscache/messagecache_test.go b/net/dnscache/messagecache_test.go
index 41fc33448..68110bf28 100644
--- a/net/dnscache/messagecache_test.go
+++ b/net/dnscache/messagecache_test.go
@@ -21,7 +21,7 @@ func TestMessageCache(t *testing.T) {
clock := tstest.NewClock(tstest.ClockOpts{
Start: time.Date(1987, 11, 1, 0, 0, 0, 0, time.UTC),
})
- mc := &MessageCache{Clock: clock.Now}
+ mc := &MessageCache{Clock: clock}
mc.SetMaxCacheSize(2)
clock.Advance(time.Second)
diff --git a/net/memnet/pipe.go b/net/memnet/pipe.go
index 471635083..683ad92a4 100644
--- a/net/memnet/pipe.go
+++ b/net/memnet/pipe.go
@@ -13,6 +13,8 @@ import (
"os"
"sync"
"time"
+
+ "tailscale.com/tstime"
)
const debugPipe = false
@@ -31,6 +33,8 @@ type Pipe struct {
writeTimeout time.Time
cancelReadTimer func()
cancelWriteTimer func()
+
+ clock tstime.Clock
}
// NewPipe creates a Pipe with a buffer size fixed at maxBuf.
@@ -38,6 +42,7 @@ func NewPipe(name string, maxBuf int) *Pipe {
p := &Pipe{
name: name,
maxBuf: maxBuf,
+ clock: tstime.StdClock{},
}
p.cnd = sync.NewCond(&p.mu)
return p
@@ -48,7 +53,7 @@ func NewPipe(name string, maxBuf int) *Pipe {
func (p *Pipe) readOrBlock(b []byte) (int, error) {
p.mu.Lock()
defer p.mu.Unlock()
- if !p.readTimeout.IsZero() && !time.Now().Before(p.readTimeout) {
+ if !p.readTimeout.IsZero() && !p.clock.Now().Before(p.readTimeout) {
return 0, os.ErrDeadlineExceeded
}
if p.blocked {
@@ -97,7 +102,7 @@ func (p *Pipe) writeOrBlock(b []byte) (int, error) {
if p.closed {
return 0, net.ErrClosed
}
- if !p.writeTimeout.IsZero() && !time.Now().Before(p.writeTimeout) {
+ if !p.writeTimeout.IsZero() && !p.clock.Now().Before(p.writeTimeout) {
return 0, os.ErrDeadlineExceeded
}
if p.blocked {
@@ -164,7 +169,7 @@ func (p *Pipe) deadlineTimer(t time.Time) func() {
if t.IsZero() {
return nil
}
- if t.Before(time.Now()) {
+ if t.Before(p.clock.Now()) {
p.cnd.Broadcast()
return nil
}
diff --git a/net/netcheck/netcheck.go b/net/netcheck/netcheck.go
index a863a5a19..63647a6d6 100644
--- a/net/netcheck/netcheck.go
+++ b/net/netcheck/netcheck.go
@@ -36,6 +36,7 @@ import (
"tailscale.com/net/stun"
"tailscale.com/syncs"
"tailscale.com/tailcfg"
+ "tailscale.com/tstime"
"tailscale.com/types/logger"
"tailscale.com/types/nettype"
"tailscale.com/types/opt"
@@ -50,6 +51,8 @@ var (
debugNetcheck = envknob.RegisterBool("TS_DEBUG_NETCHECK")
)
+var clock = tstime.StdClock{}
+
// The various default timeouts for things.
const (
// overallProbeTimeout is the maximum amount of time netcheck will
@@ -173,8 +176,7 @@ type Client struct {
// present anyway.
NetMon *netmon.Monitor
- // TimeNow, if non-nil, is used instead of time.Now.
- TimeNow func() time.Time
+ Clock tstime.Clock
// SendPacket is required to send a packet to the specified address. For
// convenience it shares a signature with WriteToUDPAddrPort.
@@ -925,11 +927,11 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap) (_ *Report,
}(probeSet)
}
- stunTimer := time.NewTimer(stunProbeTimeout)
+ stunTimer, stunTimerChannel := clock.NewTimer(stunProbeTimeout)
defer stunTimer.Stop()
select {
- case <-stunTimer.C:
+ case <-stunTimerChannel:
case <-ctx.Done():
case <-wg.DoneChan():
// All of our probes finished, so if we have >0 responses, we
@@ -1348,10 +1350,10 @@ func (c *Client) logConciseReport(r *Report, dm *tailcfg.DERPMap) {
}
func (c *Client) timeNow() time.Time {
- if c.TimeNow != nil {
- return c.TimeNow()
+ if c.Clock != nil {
+ return c.Clock.Now()
}
- return time.Now()
+ return clock.Now()
}
const (
@@ -1490,9 +1492,9 @@ func (rs *reportState) runProbe(ctx context.Context, dm *tailcfg.DERPMap, probe
}
if probe.delay > 0 {
- delayTimer := time.NewTimer(probe.delay)
+ delayTimer, delayTimerChannel := clock.NewTimer(probe.delay)
select {
- case <-delayTimer.C:
+ case <-delayTimerChannel:
case <-ctx.Done():
delayTimer.Stop()
return
@@ -1513,11 +1515,11 @@ func (rs *reportState) runProbe(ctx context.Context, dm *tailcfg.DERPMap, probe
txID := stun.NewTxID()
req := stun.Request(txID)
- sent := time.Now() // after DNS lookup above
+ sent := clock.Now() // after DNS lookup above
rs.mu.Lock()
rs.inFlight[txID] = func(ipp netip.AddrPort) {
- rs.addNodeLatency(node, ipp, time.Since(sent))
+ rs.addNodeLatency(node, ipp, clock.Since(sent))
cancelSet() // abort other nodes in this set
}
rs.mu.Unlock()
diff --git a/net/netcheck/netcheck_test.go b/net/netcheck/netcheck_test.go
index 1ed728a00..8adde53ba 100644
--- a/net/netcheck/netcheck_test.go
+++ b/net/netcheck/netcheck_test.go
@@ -398,12 +398,14 @@ func TestAddReportHistoryAndSetPreferredDERP(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeTime := time.Unix(123, 0)
+ clock := tstest.NewClock(tstest.ClockOpts{Start: fakeTime})
c := &Client{
- TimeNow: func() time.Time { return fakeTime },
+ Clock: clock,
}
dm := &tailcfg.DERPMap{HomeParams: tt.homeParams}
for _, s := range tt.steps {
fakeTime = fakeTime.Add(s.after)
+ clock.Advance(s.after)
c.addReportHistoryAndSetPreferredDERP(s.r, dm.View())
}
lastReport := tt.steps[len(tt.steps)-1].r
diff --git a/net/netmon/netmon.go b/net/netmon/netmon.go
index 4de8a47f4..9ab641831 100644
--- a/net/netmon/netmon.go
+++ b/net/netmon/netmon.go
@@ -15,6 +15,7 @@ import (
"time"
"tailscale.com/net/interfaces"
+ "tailscale.com/tstime"
"tailscale.com/types/logger"
"tailscale.com/util/set"
)
@@ -65,9 +66,10 @@ type Monitor struct {
started bool
closed bool
goroutines sync.WaitGroup
- wallTimer *time.Timer // nil until Started; re-armed AfterFunc per tick
+ wallTimer tstime.TimerController // nil until Started; re-armed AfterFunc per tick
lastWall time.Time
timeJumped bool // whether we need to send a changed=true after a big time jump
+ clock tstime.Clock
}
// ChangeFunc is a callback function registered with Monitor that's called when the
@@ -81,11 +83,12 @@ type ChangeFunc func(changed bool, state *interfaces.State)
func New(logf logger.Logf) (*Monitor, error) {
logf = logger.WithPrefix(logf, "monitor: ")
m := &Monitor{
- logf: logf,
- change: make(chan struct{}, 1),
- stop: make(chan struct{}),
- lastWall: wallTime(),
+ logf: logf,
+ change: make(chan struct{}, 1),
+ stop: make(chan struct{}),
+ clock: tstime.StdClock{},
}
+ m.lastWall = m.wallTime()
st, err := m.interfaceStateUncached()
if err != nil {
return nil, err
@@ -180,7 +183,7 @@ func (m *Monitor) Start() {
m.started = true
if shouldMonitorTimeJump {
- m.wallTimer = time.AfterFunc(pollWallTimeInterval, m.pollWallTime)
+ m.wallTimer = m.clock.AfterFunc(pollWallTimeInterval, m.pollWallTime)
}
if m.om == nil {
@@ -342,10 +345,10 @@ func jsonSummary(x any) any {
return j
}
-func wallTime() time.Time {
+func (m *Monitor) wallTime() time.Time {
// From time package's docs: "The canonical way to strip a
// monotonic clock reading is to use t = t.Round(0)."
- return time.Now().Round(0)
+ return m.clock.Now().Round(0)
}
func (m *Monitor) pollWallTime() {
@@ -374,7 +377,7 @@ func (m *Monitor) checkWallTimeAdvanceLocked() bool {
if !shouldMonitorTimeJump {
panic("unreachable") // if callers are correct
}
- now := wallTime()
+ now := m.wallTime()
if now.Sub(m.lastWall) > pollWallTimeInterval*3/2 {
m.timeJumped = true // it is reset by debounce.
}
diff --git a/net/netmon/netmon_test.go b/net/netmon/netmon_test.go
index 7d2516404..cdbd494a4 100644
--- a/net/netmon/netmon_test.go
+++ b/net/netmon/netmon_test.go
@@ -10,10 +10,12 @@ import (
"time"
"tailscale.com/net/interfaces"
+ "tailscale.com/tstest"
)
func TestMonitorStartClose(t *testing.T) {
mon, err := New(t.Logf)
+ mon.clock = &tstest.Clock{}
if err != nil {
t.Fatal(err)
}
diff --git a/net/netmon/netmon_windows.go b/net/netmon/netmon_windows.go
index 836922206..34e08889d 100644
--- a/net/netmon/netmon_windows.go
+++ b/net/netmon/netmon_windows.go
@@ -12,11 +12,13 @@ import (
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"tailscale.com/net/tsaddr"
+ "tailscale.com/tstime"
"tailscale.com/types/logger"
)
var (
errClosed = errors.New("closed")
+ clock = tstime.StdClock{}
)
type eventMessage struct {
@@ -104,11 +106,11 @@ func (m *winMon) Receive() (message, error) {
return nil, errClosed
}
- t0 := time.Now()
+ t0 := clock.Now()
select {
case msg := <-m.messagec:
- now := time.Now()
+ now := clock.Now()
m.mu.Lock()
sinceLast := now.Sub(m.lastLog)
m.lastLog = now
@@ -120,7 +122,7 @@ func (m *winMon) Receive() (message, error) {
// route updates after connecting to a large tailnet
// and all the IPv4 /32 routes.
if sinceLast > 5*time.Second || !strings.HasPrefix(msg.eventType, "ts") {
- m.logf("got windows change event after %v: evt=%s", time.Since(t0).Round(time.Millisecond), msg.eventType)
+ m.logf("got windows change event after %v: evt=%s", clock.Since(t0).Round(time.Millisecond), msg.eventType)
}
return msg, nil
case <-m.ctx.Done():
diff --git a/net/netmon/polling.go b/net/netmon/polling.go
index 9332bdde9..dfa068fe1 100644
--- a/net/netmon/polling.go
+++ b/net/netmon/polling.go
@@ -14,9 +14,12 @@ import (
"time"
"tailscale.com/net/interfaces"
+ "tailscale.com/tstime"
"tailscale.com/types/logger"
)
+var clock = tstime.StdClock{}
+
func newPollingMon(logf logger.Logf, m *Monitor) (osMon, error) {
return &pollingMon{
logf: logf,
@@ -75,7 +78,7 @@ func (pm *pollingMon) Receive() (message, error) {
pm.logf("monitor polling: Cloud Run detected, reduce polling interval to 24h")
d = 24 * time.Hour
}
- ticker := time.NewTicker(d)
+ ticker, tickerChannel := clock.NewTicker(d)
defer ticker.Stop()
base := pm.m.InterfaceState()
for {
@@ -83,7 +86,7 @@ func (pm *pollingMon) Receive() (message, error) {
return unspecifiedMessage{}, nil
}
select {
- case <-ticker.C:
+ case <-tickerChannel:
case <-pm.stop:
return nil, errors.New("stopped")
}
diff --git a/net/ping/ping.go b/net/ping/ping.go
index 170d87fb9..e13d03469 100644
--- a/net/ping/ping.go
+++ b/net/ping/ping.go
@@ -22,6 +22,7 @@ import (
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
+ "tailscale.com/tstime"
"tailscale.com/types/logger"
"tailscale.com/util/mak"
"tailscale.com/util/multierr"
@@ -59,7 +60,7 @@ type Pinger struct {
closed atomic.Bool
Logf logger.Logf
Verbose bool
- timeNow func() time.Time
+ clock tstime.Clock
id uint16 // uint16 per RFC 792
wg sync.WaitGroup
@@ -81,11 +82,11 @@ func New(ctx context.Context, logf logger.Logf, lp ListenPacketer) *Pinger {
}
return &Pinger{
- lp: lp,
- Logf: logf,
- timeNow: time.Now,
- id: binary.LittleEndian.Uint16(id[:]),
- pings: make(map[uint16]outstanding),
+ lp: lp,
+ Logf: logf,
+ clock: tstime.StdClock{},
+ id: binary.LittleEndian.Uint16(id[:]),
+ pings: make(map[uint16]outstanding),
}
}
@@ -197,7 +198,7 @@ loop:
continue
}
- p.handleResponse(buf[:n], p.timeNow(), typ)
+ p.handleResponse(buf[:n], p.clock.Now(), typ)
}
}
@@ -322,7 +323,7 @@ func (p *Pinger) Send(ctx context.Context, dest net.Addr, data []byte) (time.Dur
p.pings[seq] = outstanding{ch: ch, data: data}
p.mu.Unlock()
- start := p.timeNow()
+ start := p.clock.Now()
n, err := conn.WriteTo(b, dest)
if err != nil {
return 0, err
diff --git a/net/ping/ping_test.go b/net/ping/ping_test.go
index bbedbcad8..e60ea0eb8 100644
--- a/net/ping/ping_test.go
+++ b/net/ping/ping_test.go
@@ -284,7 +284,7 @@ func (u *udpingPacketConn) WriteTo(body []byte, dest net.Addr) (int, error) {
func mockPinger(t *testing.T, clock *tstest.Clock) (*Pinger, func()) {
p := New(context.Background(), t.Logf, nil)
- p.timeNow = clock.Now
+ p.clock = clock
p.Verbose = true
p.id = 1234
diff --git a/net/portmapper/pcp.go b/net/portmapper/pcp.go
index c1d62b02b..caa06ee35 100644
--- a/net/portmapper/pcp.go
+++ b/net/portmapper/pcp.go
@@ -128,7 +128,7 @@ func parsePCPMapResponse(resp []byte) (*pcpMapping, error) {
external := netip.AddrPortFrom(externalIP, externalPort)
lifetime := time.Second * time.Duration(res.Lifetime)
- now := time.Now()
+ now := clock.Now()
mapping := &pcpMapping{
external: external,
renewAfter: now.Add(lifetime / 2),
diff --git a/net/portmapper/portmapper.go b/net/portmapper/portmapper.go
index 2e500b555..c402b75c3 100644
--- a/net/portmapper/portmapper.go
+++ b/net/portmapper/portmapper.go
@@ -24,6 +24,7 @@ import (
"tailscale.com/net/netmon"
"tailscale.com/net/netns"
"tailscale.com/net/sockstats"
+ "tailscale.com/tstime"
"tailscale.com/types/logger"
"tailscale.com/types/nettype"
"tailscale.com/util/clientmetric"
@@ -93,6 +94,8 @@ type Client struct {
localPort uint16
mapping mapping // non-nil if we have a mapping
+
+ clock tstime.Clock
}
// mapping represents a created port-mapping over some protocol. It specifies a lease duration,
@@ -117,7 +120,7 @@ type mapping interface {
func (c *Client) HaveMapping() bool {
c.mu.Lock()
defer c.mu.Unlock()
- return c.mapping != nil && c.mapping.GoodUntil().After(time.Now())
+ return c.mapping != nil && c.mapping.GoodUntil().After(c.clock.Now())
}
// pmpMapping is an already-created PMP mapping.
@@ -170,6 +173,7 @@ func NewClient(logf logger.Logf, netMon *netmon.Monitor, debug *DebugKnobs, onCh
netMon: netMon,
ipAndGateway: interfaces.LikelyHomeRouterIP,
onChange: onChange,
+ clock: clock,
}
if debug != nil {
ret.debug = *debug
@@ -305,7 +309,7 @@ func (c *Client) sawPMPRecently() bool {
}
func (c *Client) sawPMPRecentlyLocked() bool {
- return c.pmpPubIP.IsValid() && c.pmpPubIPTime.After(time.Now().Add(-trustServiceStillAvailableDuration))
+ return c.pmpPubIP.IsValid() && c.pmpPubIPTime.After(c.clock.Now().Add(-trustServiceStillAvailableDuration))
}
func (c *Client) sawPCPRecently() bool {
@@ -315,13 +319,13 @@ func (c *Client) sawPCPRecently() bool {
}
func (c *Client) sawPCPRecentlyLocked() bool {
- return c.pcpSawTime.After(time.Now().Add(-trustServiceStillAvailableDuration))
+ return c.pcpSawTime.After(c.clock.Now().Add(-trustServiceStillAvailableDuration))
}
func (c *Client) sawUPnPRecently() bool {
c.mu.Lock()
defer c.mu.Unlock()
- return c.uPnPSawTime.After(time.Now().Add(-trustServiceStillAvailableDuration))
+ return c.uPnPSawTime.After(c.clock.Now().Add(-trustServiceStillAvailableDuration))
}
// closeCloserOnContextDone starts a new goroutine to call c.Close
@@ -373,7 +377,7 @@ func (c *Client) GetCachedMappingOrStartCreatingOne() (external netip.AddrPort,
defer c.mu.Unlock()
// Do we have an existing mapping that's valid?
- now := time.Now()
+ now := c.clock.Now()
if m := c.mapping; m != nil {
if now.Before(m.GoodUntil()) {
if now.After(m.RenewAfter()) {
@@ -443,7 +447,7 @@ func (c *Client) createOrGetMapping(ctx context.Context) (external netip.AddrPor
var prevPort uint16
// Do we have an existing mapping that's valid?
- now := time.Now()
+ now := c.clock.Now()
if m := c.mapping; m != nil {
if now.Before(m.RenewAfter()) {
defer c.mu.Unlock()
@@ -497,7 +501,7 @@ func (c *Client) createOrGetMapping(ctx context.Context) (external netip.AddrPor
}
defer uc.Close()
- uc.SetReadDeadline(time.Now().Add(portMapServiceTimeout))
+ uc.SetReadDeadline(c.clock.Now().Add(portMapServiceTimeout))
defer closeCloserOnContextDone(ctx, uc)()
pxpAddr := netip.AddrPortFrom(gw, c.pxpPort())
@@ -570,7 +574,7 @@ func (c *Client) createOrGetMapping(ctx context.Context) (external netip.AddrPor
if pres.OpCode == pmpOpReply|pmpOpMapUDP {
m.external = netip.AddrPortFrom(m.external.Addr(), pres.ExternalPort)
d := time.Duration(pres.MappingValidSeconds) * time.Second
- now := time.Now()
+ now := c.clock.Now()
m.goodUntil = now.Add(d)
m.renewAfter = now.Add(d / 2) // renew in half the time
m.epoch = pres.SecondsSinceEpoch
@@ -704,7 +708,7 @@ func (c *Client) Probe(ctx context.Context) (res ProbeResult, err error) {
if err == nil {
c.mu.Lock()
defer c.mu.Unlock()
- c.lastProbe = time.Now()
+ c.lastProbe = c.clock.Now()
}
}()
@@ -824,7 +828,7 @@ func (c *Client) Probe(ctx context.Context) (res ProbeResult, err error) {
c.logf("[v1] UPnP reply %+v, %q", meta, buf[:n])
res.UPnP = true
c.mu.Lock()
- c.uPnPSawTime = time.Now()
+ c.uPnPSawTime = c.clock.Now()
if c.uPnPMeta != meta {
c.logf("UPnP meta changed: %+v", meta)
c.uPnPMeta = meta
@@ -854,7 +858,7 @@ func (c *Client) Probe(ctx context.Context) (res ProbeResult, err error) {
if pres.OpCode == pcpOpReply|pcpOpAnnounce {
pcpHeard = true
c.mu.Lock()
- c.pcpSawTime = time.Now()
+ c.pcpSawTime = c.clock.Now()
c.mu.Unlock()
switch pres.ResultCode {
case pcpCodeOK:
@@ -893,7 +897,7 @@ func (c *Client) Probe(ctx context.Context) (res ProbeResult, err error) {
res.PMP = true
c.mu.Lock()
c.pmpPubIP = pres.PublicAddr
- c.pmpPubIPTime = time.Now()
+ c.pmpPubIPTime = c.clock.Now()
c.pmpLastEpoch = pres.SecondsSinceEpoch
c.mu.Unlock()
continue
diff --git a/net/portmapper/upnp.go b/net/portmapper/upnp.go
index 34cae5840..431eeed17 100644
--- a/net/portmapper/upnp.go
+++ b/net/portmapper/upnp.go
@@ -24,9 +24,12 @@ import (
"github.com/tailscale/goupnp/dcps/internetgateway2"
"tailscale.com/control/controlknobs"
"tailscale.com/net/netns"
+ "tailscale.com/tstime"
"tailscale.com/types/logger"
)
+var clock = tstime.StdClock{}
+
// References:
//
// WANIP Connection v2: http://upnp.org/specs/gw/UPnP-gw-WANIPConnection-v2-Service.pdf
@@ -278,7 +281,7 @@ func (c *Client) getUPnPPortMapping(
return netip.AddrPort{}, false
}
- now := time.Now()
+ now := c.clock.Now()
upnp := &upnpMapping{
gw: gw,
internal: internal,
diff --git a/net/proxymux/mux.go b/net/proxymux/mux.go
index ff5aaff3b..bcd2810db 100644
--- a/net/proxymux/mux.go
+++ b/net/proxymux/mux.go
@@ -13,8 +13,12 @@ import (
"net"
"sync"
"time"
+
+ "tailscale.com/tstime"
)
+var clock = tstime.StdClock{}
+
// SplitSOCKSAndHTTP accepts connections on ln and passes connections
// through to either socksListener or httpListener, depending the
// first byte sent by the client.
@@ -48,7 +52,7 @@ func splitSOCKSAndHTTPListener(ln net.Listener, sl, hl *listener) {
}
func routeConn(c net.Conn, socksListener, httpListener *listener) {
- if err := c.SetReadDeadline(time.Now().Add(15 * time.Second)); err != nil {
+ if err := c.SetReadDeadline(clock.Now().Add(15 * time.Second)); err != nil {
c.Close()
return
}
diff --git a/net/sockstats/sockstats_tsgo.go b/net/sockstats/sockstats_tsgo.go
index 37edddddf..f3828824c 100644
--- a/net/sockstats/sockstats_tsgo.go
+++ b/net/sockstats/sockstats_tsgo.go
@@ -13,10 +13,10 @@ import (
"sync"
"sync/atomic"
"syscall"
- "time"
"tailscale.com/net/interfaces"
"tailscale.com/net/netmon"
+ "tailscale.com/tstime"
"tailscale.com/types/logger"
"tailscale.com/util/clientmetric"
)
@@ -318,7 +318,7 @@ type radioMonitor struct {
// startTime is the time we started tracking radio usage.
startTime int64
- now func() time.Time
+ clock tstime.Clock
}
// radioSampleSize is the number of samples to store and report for cellular radio usage.
@@ -329,14 +329,16 @@ const radioSampleSize = 3600 // 1 hour
// Otherwise, all clients will report 100% radio usage on startup.
var initStallPeriod int64 = 120 // 2 minutes
+var clock = tstime.StdClock{}
+
var radio = &radioMonitor{
- now: time.Now,
- startTime: time.Now().Unix(),
+ clock: clock,
+ startTime: clock.Now().Unix(),
}
// radioActivity should be called whenever network activity occurs on a cellular network interface.
func (rm *radioMonitor) active() {
- t := rm.now().Unix()
+ t := rm.clock.Now().Unix()
rm.usage[t%radioSampleSize] = t
}
@@ -392,7 +394,7 @@ func (rm *radioMonitor) radioHighPercent() int64 {
// forEachSample calls f for each sample in the past hour (or less if less time
// has passed -- the evaluated period is returned, measured in seconds)
func (rm *radioMonitor) forEachSample(f func(c int, isActive bool)) (periodLength int64) {
- now := rm.now().Unix()
+ now := rm.clock.Now().Unix()
periodLength = radioSampleSize
if t := now - rm.startTime; t < periodLength {
if t <= 0 {
diff --git a/net/tlsdial/tlsdial.go b/net/tlsdial/tlsdial.go
index d571d38a6..df5569017 100644
--- a/net/tlsdial/tlsdial.go
+++ b/net/tlsdial/tlsdial.go
@@ -20,10 +20,10 @@ import (
"os"
"sync"
"sync/atomic"
- "time"
"tailscale.com/envknob"
"tailscale.com/health"
+ "tailscale.com/tstime"
)
var counterFallbackOK int32 // atomic
@@ -41,6 +41,8 @@ var debug = envknob.RegisterBool("TS_DEBUG_TLS_DIAL")
// Headscale, etc.
var tlsdialWarningPrinted sync.Map // map[string]bool
+var clock = tstime.StdClock{}
+
// Config returns a tls.Config for connecting to a server.
// If base is non-nil, it's cloned as the base config before
// being configured and returned.
@@ -166,7 +168,7 @@ func SetConfigExpectedCert(c *tls.Config, certDNSName string) {
certs[i] = cert
}
opts := x509.VerifyOptions{
- CurrentTime: time.Now(),
+ CurrentTime: clock.Now(),
DNSName: certDNSName,
Intermediates: x509.NewCertPool(),
}
diff --git a/net/tshttpproxy/tshttpproxy.go b/net/tshttpproxy/tshttpproxy.go
index 24b0050e9..21b859b57 100644
--- a/net/tshttpproxy/tshttpproxy.go
+++ b/net/tshttpproxy/tshttpproxy.go
@@ -19,6 +19,7 @@ import (
"time"
"golang.org/x/net/http/httpproxy"
+ "tailscale.com/tstime"
)
// InvalidateCache invalidates the package-level cache for ProxyFromEnvironment.
@@ -37,6 +38,8 @@ var (
proxyFunc func(*url.URL) (*url.URL, error)
)
+var clock = tstime.StdClock{}
+
func getProxyFunc() func(*url.URL) (*url.URL, error) {
// Create config/proxyFunc if it's not created
mu.Lock()
@@ -54,7 +57,7 @@ func getProxyFunc() func(*url.URL) (*url.URL, error) {
func setNoProxyUntil(d time.Duration) {
mu.Lock()
defer mu.Unlock()
- noProxyUntil = time.Now().Add(d)
+ noProxyUntil = clock.Now().Add(d)
}
var _ = setNoProxyUntil // quiet staticcheck; Windows uses the above, more might later
@@ -130,7 +133,7 @@ func ProxyFromEnvironment(req *http.Request) (*url.URL, error) {
mu.Lock()
noProxyTime := noProxyUntil
mu.Unlock()
- if time.Now().Before(noProxyTime) {
+ if clock.Now().Before(noProxyTime) {
return nil, nil
}
diff --git a/net/tshttpproxy/tshttpproxy_windows.go b/net/tshttpproxy/tshttpproxy_windows.go
index 06a1f5ae4..36e49f2d8 100644
--- a/net/tshttpproxy/tshttpproxy_windows.go
+++ b/net/tshttpproxy/tshttpproxy_windows.go
@@ -131,9 +131,9 @@ func proxyFromWinHTTP(ctx context.Context, urlStr string) (proxy *url.URL, err e
}
defer whi.Close()
- t0 := time.Now()
+ t0 := clock.Now()
v, err := whi.GetProxyForURL(urlStr)
- td := time.Since(t0).Round(time.Millisecond)
+ td := clock.Since(t0).Round(time.Millisecond)
if err := ctx.Err(); err != nil {
log.Printf("tshttpproxy: winhttp: context canceled, ignoring GetProxyForURL(%q) after %v", urlStr, td)
return nil, err
diff --git a/net/tstun/ifstatus_windows.go b/net/tstun/ifstatus_windows.go
index fd9fc2112..f9ace2ed8 100644
--- a/net/tstun/ifstatus_windows.go
+++ b/net/tstun/ifstatus_windows.go
@@ -79,9 +79,9 @@ func waitInterfaceUp(iface tun.Device, timeout time.Duration, logf logger.Logf)
}
defer cb.Unregister()
- t0 := time.Now()
+ t0 := clock.Now()
expires := t0.Add(timeout)
- ticker := time.NewTicker(10 * time.Second)
+ ticker, tickerChannel := clock.NewTicker(10 * time.Second)
defer ticker.Stop()
for {
@@ -89,19 +89,19 @@ func waitInterfaceUp(iface tun.Device, timeout time.Duration, logf logger.Logf)
select {
case <-iw.sig:
- iw.logf("TUN interface is up after %v", time.Since(t0))
+ iw.logf("TUN interface is up after %v", clock.Since(t0))
return nil
- case <-ticker.C:
+ case <-tickerChannel:
}
if iw.isUp() {
// Very unlikely to happen - either NotifyIpInterfaceChange doesn't work
// or it came up in the same moment as tick. Indicate this in the log message.
- iw.logf("TUN interface is up after %v (on poll, without notification)", time.Since(t0))
+ iw.logf("TUN interface is up after %v (on poll, without notification)", clock.Since(t0))
return nil
}
- if expires.Before(time.Now()) {
+ if expires.Before(clock.Now()) {
iw.logf("timeout waiting %v for TUN interface to come up", timeout)
return fmt.Errorf("timeout waiting for TUN interface to come up")
}
diff --git a/net/tstun/tun.go b/net/tstun/tun.go
index b31ffa7ca..d6c7b9cfc 100644
--- a/net/tstun/tun.go
+++ b/net/tstun/tun.go
@@ -14,12 +14,15 @@ import (
"time"
"github.com/tailscale/wireguard-go/tun"
+ "tailscale.com/tstime"
"tailscale.com/types/logger"
)
// createTAP is non-nil on Linux.
var createTAP func(tapName, bridgeName string) (tun.Device, error)
+var clock = tstime.StdClock{}
+
// 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) {
diff --git a/net/tstun/wrap.go b/net/tstun/wrap.go
index 20f54744d..3ddf3864c 100644
--- a/net/tstun/wrap.go
+++ b/net/tstun/wrap.go
@@ -28,6 +28,7 @@ import (
"tailscale.com/net/tsaddr"
"tailscale.com/net/tstun/table"
"tailscale.com/syncs"
+ "tailscale.com/tstime"
"tailscale.com/tstime/mono"
"tailscale.com/types/ipproto"
"tailscale.com/types/key"
@@ -94,9 +95,7 @@ type Wrapper struct {
destMACAtomic syncs.AtomicValue[[6]byte]
discoKey syncs.AtomicValue[key.DiscoPublic]
- // timeNow, if non-nil, will be used to obtain the current time.
- timeNow func() time.Time
-
+ clock tstime.Clock
// natV4Config stores the current NAT configuration.
natV4Config atomic.Pointer[natV4Config]
@@ -262,13 +261,13 @@ func wrap(logf logger.Logf, tdev tun.Device, isTAP bool) *Wrapper {
return w
}
-// now returns the current time, either by calling t.timeNow if set or time.Now
+// now returns the current time, either by calling t.clock.Now if set or clock.Now
// if not.
func (t *Wrapper) now() time.Time {
- if t.timeNow != nil {
- return t.timeNow()
+ if t.clock != nil {
+ return t.clock.Now()
}
- return time.Now()
+ return clock.Now()
}
// SetDestIPActivityFuncs sets a map of funcs to run per packet
diff --git a/net/tstun/wrap_test.go b/net/tstun/wrap_test.go
index f9e35beec..97284b9a1 100644
--- a/net/tstun/wrap_test.go
+++ b/net/tstun/wrap_test.go
@@ -818,9 +818,7 @@ func TestCaptureHook(t *testing.T) {
now := time.Unix(1682085856, 0)
_, w := newFakeTUN(t.Logf, true)
- w.timeNow = func() time.Time {
- return now
- }
+ w.clock = tstest.NewClock(tstest.ClockOpts{Start: now})
w.InstallCaptureHook(hook)
defer w.Close()