profile
viewpoint
If you are wondering where the data of this site comes from, please visit https://api.github.com/users/crawshaw/events. GitMemory does not store any data, but only uses NGINX to cache data for a period of time. The idea behind GitMemory is simply to give users a better reading experience.
David Crawshaw crawshaw Tailscale Berkeley, CA https://crawshaw.io Tailscaler

bradfitz/goimports 987

(old repo) Tool to fix (add, remove) your Go imports automatically.

crawshaw/littleboss 623

littleboss: supervisor construction kit

crawshaw/iox 53

Go I/O utilities

crawshaw/balloon 47

experiments with animations and data formats. parts will be submitted to go.mobile if they work.

crawshaw/asm 31

amd64 (x86-64) assembler written in Go

crawshaw/fs 15

File system package that supports cancellation.

crawshaw/apk 9

Android APK writer

crawshaw/gopher3d 8

Simple android app with a 3D spinning gopher

crawshaw/gofill 3

gofill is an experimental hinting service for text editors

PR opened tailscale/tailscale

Reviewers
net/dns/resolver: EDNS OPT record off-by-one

I don't know how to get access to a real packet. Basing this commit entirely off:

       +------------+--------------+------------------------------+
       | Field Name | Field Type   | Description                  |
       +------------+--------------+------------------------------+
       | NAME       | domain name  | MUST be 0 (root domain)      |
       | TYPE       | u_int16_t    | OPT (41)                     |
       | CLASS      | u_int16_t    | requestor's UDP payload size |
       | TTL        | u_int32_t    | extended RCODE and flags     |
       | RDLEN      | u_int16_t    | length of all RDATA          |
       | RDATA      | octet stream | {attribute,value} pairs      |
       +------------+--------------+------------------------------+

From https://datatracker.ietf.org/doc/html/rfc6891#section-6.1.2

Signed-off-by: David Crawshaw crawshaw@tailscale.com

+3 -2

0 comment

1 changed file

pr created time in 9 hours

create barnchtailscale/tailscale

branch : crawshaw/edns

created branch time in 9 hours

Pull request review commenttailscale/tailscale

net/tstun: buffer outbound channel

 func Wrap(logf logger.Logf, tdev tun.Device) *Wrapper { 		// a goroutine should not block when setting it, even with no listeners. 		bufferConsumed: make(chan struct{}, 1), 		closed:         make(chan struct{}),-		outbound:       make(chan tunReadResult),+		outbound:       make(chan tunReadResult, 1),

This needs a comment saying that it can be unbuffered, but the buffer improves performance.

Did you try with other numbers, or is 1 all the protocol allows?

josharian

comment created time in 11 hours

PullRequestReviewEvent
PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commenttailscale/tailscale

net/dnscache: make Dialer try all resolved IPs

 func Dialer(fwd DialContextFunc, dnsCache *Resolver) DialContextFunc { 				// Return with original error 				return 			}-			for _, ip := range ips {-				dst := net.JoinHostPort(ip.String(), port)-				if c, err := fwd(ctx, network, dst); err == nil {-					retConn = c-					ret = nil-					return-				}+			if c, err := raceDial(ctx, fwd, network, ips, port); err == nil {+				retConn = c+				ret = nil+				return 			} 		}() -		ip, ip6, _, err := dnsCache.LookupIP(ctx, host)+		ip, ip6, allIPs, err := dnsCache.LookupIP(ctx, host) 		if err != nil { 			return nil, fmt.Errorf("failed to resolve %q: %w", host, err) 		}-		dst := net.JoinHostPort(ip.String(), port)-		if debug {-			log.Printf("dnscache: dialing %s, %s for %s", network, dst, address)+		i4s := v4addrs(allIPs)+		if len(i4s) < 2 {

So if we serve exactly one ipv4 address, we try ipv4 and fall back to ipv6. If we serve multiple ipv4 addresses, we race ipv4 and ipv6 and take whichever comes back first. Does the fact we aren't favoring ipv4 in the latter case cause problems?

That is: are we happy treating ipv6 as an equal citizen on a dual stack system? Many systems in the past have favored the ipv4 connection on the theory that ipv6 is not as well maintained by dual stack ISPs and produces unexpected failures. Are those days behind us, as in, does chrome treat ipv6 equally?

bradfitz

comment created time in a day

Pull request review commenttailscale/tailscale

net/dnscache: make Dialer try all resolved IPs

 func Dialer(fwd DialContextFunc, dnsCache *Resolver) DialContextFunc { 				// Return with original error 				return 			}-			for _, ip := range ips {-				dst := net.JoinHostPort(ip.String(), port)-				if c, err := fwd(ctx, network, dst); err == nil {-					retConn = c-					ret = nil-					return-				}+			if c, err := raceDial(ctx, fwd, network, ips, port); err == nil {+				retConn = c+				ret = nil+				return 			} 		}() -		ip, ip6, _, err := dnsCache.LookupIP(ctx, host)+		ip, ip6, allIPs, err := dnsCache.LookupIP(ctx, host) 		if err != nil { 			return nil, fmt.Errorf("failed to resolve %q: %w", host, err) 		}-		dst := net.JoinHostPort(ip.String(), port)-		if debug {-			log.Printf("dnscache: dialing %s, %s for %s", network, dst, address)+		i4s := v4addrs(allIPs)+		if len(i4s) < 2 {+			dst := net.JoinHostPort(ip.String(), port)+			if debug {+				log.Printf("dnscache: dialing %s, %s for %s", network, dst, address)+			}+			c, err := fwd(ctx, network, dst)+			if err == nil || ctx.Err() != nil || ip6 == nil {+				return c, err+			}+			// Fall back to trying IPv6.+			dst = net.JoinHostPort(ip6.String(), port)+			return fwd(ctx, network, dst)+		}++		// Multiple IPv4 candidates, and 0+ IPv6.+		ipsToTry := append(i4s, v6addrs(allIPs)...)+		return raceDial(ctx, fwd, network, ipsToTry, port)+	}+}++// fallbackDelay is how long to wait between trying subsequent+// addresses when multiple options are available.+// 300ms is the same as Go's Happy Eyeballs fallbackDelay value.+const fallbackDelay = 300 * time.Millisecond++// raceDial tries to dial port on each ip in ips, starting a new race+// dial every 300ms apart, returning whichever completes first.

could s/300ms/fallbackDelay/ in the comments so there's less chance of them getting out of date in the future

bradfitz

comment created time in a day

PullRequestReviewEvent
PullRequestReviewEvent
PullRequestReviewEvent
PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commenttailscale/tailscale

wgengine: re-set DNS config on Linux after a major link change

 func (e *userspaceEngine) linkChange(changed bool, cur *interfaces.State) { 	health.SetAnyInterfaceUp(up) 	e.magicConn.SetNetworkUp(up) +	// Hacky workaround for Linux DNS issue 2458: on+	// suspend/resumse or whenever NetworkManager is started, it+	// nukes all systemd-resolved configs. So reapply our DNS+	// config on major link change.+	if runtime.GOOS == "linux" && changed {

Elsewhere we have an issue suggesting we fsnotify resolv.conf and rewrite it on change, so we always win. I think that’s generally the right attitude: if we think we own it, own it.

bradfitz

comment created time in 2 days

PullRequestReviewEvent

push eventtailscale/sqlite

David Crawshaw

commit sha 7436ebf1c6760b2d01064943465bb27a61e0ec66

sqlite, etc: add tracing API Our expected use of this will be as an "event" API, not a tracing API. But for future users, I included a traceID, that could be used to retrofit one of those large trace/span systems onto the driver if you were so inclined. Minimal benchmark impact: Persist-24 1.93µs ± 1% 1.92µs ± 1% -0.51% (p=0.000 n=19+18) EmptyExec-24 386ns ± 0% 401ns ± 1% +3.85% (p=0.000 n=17+20) Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 6 days

PR merged tailscale/sqlite

sqlite, etc: add tracing API

Our expected use of this will be as an "event" API, not a tracing API. But for future users, I included a traceID, that could be used to retrofit one of those large trace/span systems onto the driver if you were so inclined.

Minimal benchmark impact:

Persist-24    1.93µs ± 1%  1.92µs ± 1%  -0.51%  (p=0.000 n=19+18)
EmptyExec-24   386ns ± 0%   401ns ± 1%  +3.85%  (p=0.000 n=17+20)

Signed-off-by: David Crawshaw crawshaw@tailscale.com

+246 -39

0 comment

5 changed files

crawshaw

pr closed time in 6 days

push eventtailscale/sqlite

David Crawshaw

commit sha 02bd521feb5322e0514833b67c0fa2fce3d11e98

sqlite: use sqliteh.OpenFlagsDefault Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

David Crawshaw

commit sha 3676a807c2e1a4b706bb0c26cb55327dff379c56

sqlite, etc: add BusyTimeout I am somewhat at a loss for why setting a busy timeout seems to be required for standard concurrent operation of writers. According to the documentation: https://sqlite.org/wal.html#sometimes_queries_return_sqlite_busy_in_wal_mode The conditions under which a WAL database should block committing transactions should be rare. None of the test cases I have written trigger those conditions. So why do I have to set a busy timeout to avoid almost all concurrent write transactions getting caught on SQLITE_BUSY? Anyway, it is a mystery that can be investigated slowly. The busy timeout does not inhibit good throughput, it is easy to reach 50k write transactions per second on a good SSD, with this busy timeout set. Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

David Crawshaw

commit sha e7f150aa8547c35888dc9d43da77f028c06d2112

sqlite: annotate more errors with details Found the hard way, trying to debug real programs. Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

David Crawshaw

commit sha 7b860b90c05d756095622fcea9dd30893c382558

cgosqlite: add .ignore file for ripgrep Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

David Crawshaw

commit sha fc0108c08c5bd0fb544e58c8c2ba8b1c82adfb8e

cgosqlite: import SQLite amalgamation v3.36.0 Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

David Crawshaw

commit sha 36ffd6c03a4e07c25384dba5f8fd54974a0e80e1

sqlite: update docs for in-memory mode in v3.36 Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

David Crawshaw

commit sha dbcdbc95f7845234b95868c2b64ab6f1b606f82b

sqlite, etc: add tracing API Our expected use of this will be as an "event" API, not a tracing API. But for future users, I included a traceID, that could be used to retrofit one of those large trace/span systems onto the driver if you were so inclined. Minimal benchmark impact: Persist-24 1.93µs ± 1% 1.92µs ± 1% -0.51% (p=0.000 n=19+18) EmptyExec-24 386ns ± 0% 401ns ± 1% +3.85% (p=0.000 n=17+20) Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 6 days

push eventtailscale/sqlite

David Crawshaw

commit sha 7ae51f5356cd302923c05d64c37d681efd62a40a

sqlite, etc: add tracing API Our expected use of this will be as an "event" API, not a tracing API. But for future users, I included a traceID, that could be used to retrofit one of those large trace/span systems onto the driver if you were so inclined. Minimal benchmark impact: Persist-24 1.93µs ± 1% 1.92µs ± 1% -0.51% (p=0.000 n=19+18) EmptyExec-24 386ns ± 0% 401ns ± 1% +3.85% (p=0.000 n=17+20) Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 7 days

Pull request review commenttailscale/sqlite

sqlite, etc: add tracing API

 func (stmt *Stmt) ClearBindings() error { 	return errCode(C.sqlite3_clear_bindings(stmt.stmt)) } -func (stmt *Stmt) ResetAndClear() error {-	return errCode(C.reset_and_clear(stmt.stmt))+func (stmt *Stmt) ResetAndClear() (time.Duration, error) {+	if stmt.start != (C.struct_timespec{}) {+		var d C.int64_t+		err := errCode(C.reset_and_clear(stmt.stmt, &stmt.start, &d))

Fixed by cheating, I put some scratch variables inside the *Stmt object that is already heap allocated. (Use of it is single-threaded.)

crawshaw

comment created time in 7 days

PullRequestReviewEvent

Pull request review commenttailscale/tailscale

wgengine/{monitor,router}: restore Linux ip rules when systemd deletes them

 func newUserspaceRouterAdvanced(logf logger.Logf, tunname string, linkMon *monit 		ipt4: netfilter4, 		ipt6: netfilter6, 		cmd:  cmd,-	}, nil+	}++	return r, nil }  func (r *linuxRouter) Up() error {+	if r.unregLinkMon == nil && r.linkMon != nil {+		r.unregLinkMon = r.linkMon.RegisterRuleDeleteCallback(func(table uint8, priority uint32) {+			if priority >= 5200 && priority < 5300 {

not checking the table?

bradfitz

comment created time in 7 days

Pull request review commenttailscale/tailscale

wgengine/{monitor,router}: restore Linux ip rules when systemd deletes them

 func newUserspaceRouterAdvanced(logf logger.Logf, tunname string, linkMon *monit 		ipt4: netfilter4, 		ipt6: netfilter6, 		cmd:  cmd,-	}, nil+	}++	return r, nil }  func (r *linuxRouter) Up() error {+	if r.unregLinkMon == nil && r.linkMon != nil {+		r.unregLinkMon = r.linkMon.RegisterRuleDeleteCallback(func(table uint8, priority uint32) {+			if priority >= 5200 && priority < 5300 {+				r.logf("somebody (systemd-networkd?) deleted Tailscale's ip rules; restoring")+				r.addIPRules()

is addIPRules robust when only some of the rules are deleted?

bradfitz

comment created time in 7 days

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commenttailscale/tailscale

wgengine/router: take a link monitor

 ip route add throw 192.168.0.0/24 table 52` + basic, 		}, 	} +	mon, err := monitor.New(logger.Discard)+	if err != nil {+		t.Fatal(err)+	}+	mon.Start()+	defer mon.Close()+ 	fake := NewFakeOS(t)-	router, err := newUserspaceRouterAdvanced(t.Logf, "tailscale0", fake.netfilter4, fake.netfilter6, fake, true, true)+	router, err := newUserspaceRouterAdvanced(t.Logf, "tailscale0", mon, fake.netfilter4, fake.netfilter6, fake, true, true)

not going to cause test flakes when a linkchange comes through during the test?

bradfitz

comment created time in 7 days

Pull request review commenttailscale/tailscale

wgengine/router: take a link monitor

 package router  import (+	"fmt"+	"runtime"+ 	"golang.zx2c4.com/wireguard/tun" 	"tailscale.com/types/logger"+	"tailscale.com/wgengine/monitor" ) -func newUserspaceRouter(logf logger.Logf, tunname string, tunDev tun.Device, netChanged func()) Router {-	return NewFakeRouter(logf, tunname, tunDev, netChanged)+func newUserspaceRouter(logf logger.Logf, tunname string, tunDev tun.Device, linkMon *monitor.Mon) Router {+	panic(fmt.Sprintf("unsupported OS %q", runtime.GOOS))

is there a netstack-only os this is going to break?

bradfitz

comment created time in 7 days

PullRequestReviewEvent
PullRequestReviewEvent

create barnchcrawshaw/rand

branch : main

created branch time in 12 days