profile
viewpoint
David Crawshaw crawshaw Tailscale New York, NY https://crawshaw.io cofounder of Tailscale

bradfitz/goimports 964

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

crawshaw/littleboss 626

littleboss: supervisor construction kit

crawshaw/iox 50

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/saws 15

Scala library interface for Amazon S3, SimpleDB, SQS

crawshaw/apk 10

Android APK writer

crawshaw/gopher3d 9

Simple android app with a 3D spinning gopher

issue commenttailscale/tailscale

stun: index out of range

Ah that's the ipv6 path. it's under-tested because I wrote that STUN code against public servers I could find, none of which returned interesting ipv6 data to me (because my ISP doesn't do it).

Probably easier for you to take this, as you have lots of ipv6.

bradfitz

comment created time in an hour

push eventtailscale/tailscale

David Crawshaw

commit sha 44670d0da97f1e0f77f3f6b1489eb3f32db679fb

wgengine: revert wgdev.Close on Close from last commit Causes as-yet-unknown problems in some tests. Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

David Crawshaw

commit sha 6d2ac014641b9ef86cf3df4f135e297f87caf3ba

go.mod: bump wireguard-go version Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 16 hours

push eventtailscale/wireguard-go

David Crawshaw

commit sha 3ec48fad1002d44542f77e23525801c5e5ff2d10

device: SendPacket: handle peer==nil and improve errors Signed-off-by: David Crawshaw <david@zentus.com>

view details

push time in 16 hours

push eventtailscale/tailscale

wardn

commit sha 9390a3ef55ceb620ef7186142995c03116358e02

wgengine: properly clean up freebsd routes and interfaces on close Signed-off-by: wardn <wardn@users.noreply.github.com>

view details

push time in 17 hours

PR merged tailscale/tailscale

Reviewers
wgengine: properly clean up freebsd routes and interfaces on close

The routes and interfaces will get cleaned up on close, so there's no need to try updating interfaces and routes with empty addresses. device.Close() will properly close and clean up the device.

this resolves #72

+6 -9

1 comment

3 changed files

wardn

pr closed time in 17 hours

issue closedtailscale/tailscale

freebsd: "interface wg0 already exists"

Is your feature request related to a problem? Please describe.

NewUserspaceEngine in wgengine/userspace.go issues a call to CreateTUN, which creates the wg0 interface on first run, but results in an "interface wg0 already exists" on subsequent runs.

Describe the solution you'd like

Mostly just looking for some context on whether this scenario exists on other platforms and how you'd like it to be handled. Perhaps a way to handle CreateTUN in which if it already exists, it gets reused.

Describe alternatives you've considered

Looking at the wireguard-go implementation, it looks like they're setting an environment variable after creating the tun, and referencing the file descriptor in the environment variable on subsequent runs. I can issue an "ifconfig wg0 destroy" to clean up the interface on close but that seems a little brute-forcish, and handling it higher up will likely impact all the platforms. I've checked the linux/darwin/windows implementations for an example of how this is handled but it's not immediately apparent from the code.

Additional context

logtail... Starting userspace wireguard engine. external packet routing via --tun=wg0 enabled CreateTUN: interface wg0 already exists Error starting wireguard engine: interface wg0 already exists

closed time in 17 hours

wardn

Pull request review commenttailscale/tailscale

ipn: fix some mutex/ownership issues

 func (b *LocalBackend) runPoller() { }  func (b *LocalBackend) send(n Notify) {-	if b.notify != nil {+	b.mu.Lock()+	notify := b.notify+	b.mu.Unlock()+	if notify != nil {

I like Nigel Tao's policy of making a lock/unlock block its own paragraph.

bradfitz

comment created time in 17 hours

push eventtailscale/tailscale

David Crawshaw

commit sha 7a3be96199759987773c26859d4478bbbda6eafe

wgengine: add pinger to generate initial spray packets For 3 seconds after a successful handshake, wgengine will send a ping packet every 300ms to its peer. This ensures the spray logic in magicsock has something to spray. Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 18 hours

PR merged tailscale/tailscale

Reviewers
wgengine: add pinger to generate initial spray packets

For 3 seconds after a successful handshake, wgengine will send a ping packet every 300ms to its peer. This ensures the spray logic in magicsock has something to spray.

+108 -6

2 comments

2 changed files

crawshaw

pr closed time in 18 hours

pull request commenttailscale/tailscale

wgengine: add pinger to generate initial spray packets

All done.

crawshaw

comment created time in 19 hours

push eventtailscale/tailscale

David Crawshaw

commit sha ec101e4d5498e6ef5ada136d933e68a455e76c4e

wgengine: add pinger to generate initial spray packets For 3 seconds after a successful handshake, wgengine will send a ping packet every 300ms to its peer. This ensures the spray logic in magicsock has something to spray. Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 19 hours

PR opened tailscale/tailscale

Reviewers
wgengine: add pinger to generate initial spray packets

For 3 seconds after a successful handshake, wgengine will send a ping packet every 300ms to its peer. This ensures the spray logic in magicsock has something to spray.

+94 -6

0 comment

2 changed files

pr created time in a day

push eventtailscale/tailscale

Brad Fitzpatrick

commit sha 8696b17b5fcc32b7076a05cb270036dbafa44d74

wgengine/magicsock: turn off DERP log spamminess by default Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>

view details

David Crawshaw

commit sha a6ad3c46e2916a633a7f4a50740ce690880ca063

magicsock: spray some normal packets after a handshake In particular, this is designed to catch the case where a HandshakeInitiation packet is sent out but the intermediate NATs have not been primed, so the packet passes over DERP. In that case, the HandshakeResponse also comes back over DERP, and the connection proceeds via DERP without ever trying to punch through the NAT. With this change, the HandshakeResponse (which was sprayed out and so primed one NAT) triggers an UpdateDst, which triggers the extra spray logic. (For this to work, there has to be an initial supply of packets to send on to a peer for the three seconds following a handshake. The source of these packets is left as a future exercise.) Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

David Crawshaw

commit sha 4ea9ee6cac94603642f317cc8dd60b80d38105a0

wgengine: add pinger to generate initial spray packets For 3 seconds after a successful handshake, wgengine will send a ping packet every 300ms to its peer. This ensures the spray logic in magicsock has something to spray. Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in a day

push eventtailscale/wireguard-go

David Crawshaw

commit sha bf57f94d4d4873a8e7c2067104ce5c62a739c347

device: add peer parameters to HandshakeDone

view details

David Crawshaw

commit sha 22c50ed0a0860321e80ab2864483559fa7f3b0e0

device: add a SendPacket method This is useful when using wireguard-go as a library. Signed-off-by: David Crawshaw <david@zentus.com>

view details

push time in a day

issue openedtailscale/tailscale

wgengine: rate limiter in `wgengine.RequestStatus`

This gets called too frequently on hello.ipn.dev, eating CPU time.

created time in a day

issue commenttailscale/tailscale

wireguard-go/wgcfg: write good implementation of CIDR.Contains

If you send a PR and I rebase it out from under you, I'll rebase your commit for you (and I'll make sure to preserve it as a separate commit from anything we do).

I'm going through a lot of rebasing in preparation for reviewing commits and sending them upstream. It's not pain I want anyone else to be subject to.

bradfitz

comment created time in 2 days

push eventtailscale/tailscale

David Crawshaw

commit sha a6ad3c46e2916a633a7f4a50740ce690880ca063

magicsock: spray some normal packets after a handshake In particular, this is designed to catch the case where a HandshakeInitiation packet is sent out but the intermediate NATs have not been primed, so the packet passes over DERP. In that case, the HandshakeResponse also comes back over DERP, and the connection proceeds via DERP without ever trying to punch through the NAT. With this change, the HandshakeResponse (which was sprayed out and so primed one NAT) triggers an UpdateDst, which triggers the extra spray logic. (For this to work, there has to be an initial supply of packets to send on to a peer for the three seconds following a handshake. The source of these packets is left as a future exercise.) Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 2 days

PR merged tailscale/tailscale

magicsock: spray some packets after an endpoint change

Intended to help with upgrading from DERP to direct UDP connections.

In particular, this is designed to catch the case where a HandshakeInitiation packet is sent out but the intermediate NATs have not been primed, so the packet passes over DERP. In that case, the HandshakeResponse also comes back over DERP, and the connection proceeds via DERP without ever trying to punch through the NAT.

With this change, the HandshakeResponse (which was sprayed out and so primed one NAT) triggers an UpdateDst, which marks the next 5 non-handshake packets for spraying. At this point both NATs are primed, one of these 5 packets will make it through, triggering an UpdateDst on the other side and then both sides will be talking directly without DERP.

+34 -2

4 comments

1 changed file

crawshaw

pr closed time in 2 days

push eventtailscale/tailscale

David Crawshaw

commit sha 3f33ed80fd25d26592378d61afc5e01205396d97

magicsock: spray some normal packets after a handshake In particular, this is designed to catch the case where a HandshakeInitiation packet is sent out but the intermediate NATs have not been primed, so the packet passes over DERP. In that case, the HandshakeResponse also comes back over DERP, and the connection proceeds via DERP without ever trying to punch through the NAT. With this change, the HandshakeResponse (which was sprayed out and so primed one NAT) triggers an UpdateDst, which triggers the extra spray logic. (For this to work, there has to be an initial supply of packets to send on to a peer for the three seconds following a handshake. The source of these packets is left as a future exercise.) Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 2 days

pull request commenttailscale/tailscale

magicsock: spray some packets after an endpoint change

PTAL

Modified this PR to spray a packet every 250ms for 3 seconds after a handshake.

Working on other code to actually generate some content to make sure there's something to send. This should be safe to commit without that extra code that generates packets (though it won't generally solve the DERP upgrade problem without it).

crawshaw

comment created time in 2 days

push eventtailscale/tailscale

wardn

commit sha c51b8c206d97b696c95ba54b9a44ea606c12fc0e

wgengine: resolv.conf spelling corrections Signed-off-by: wardn <wardn@users.noreply.github.com>

view details

David Crawshaw

commit sha 8994a59e2011b1b12ec10ecb22f97ae0da9ce3c9

go.mod: update wireguard-go version Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

David Crawshaw

commit sha 868cfae84fa32f5332e53687e99f125a100f54c1

wgengine, magicsock: adjust for wireguard-go conn/device package split Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

Brad Fitzpatrick

commit sha 7a3b91390b6c26dc2a5258599827fbdc527d853c

wgengine/magicsock: fix crash in Send when Endpoint isn't an AddrSet Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>

view details

Brad Fitzpatrick

commit sha f473965ca1117c80e56e6326d85007d0c8b09085

go.sum: update Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>

view details

David Anderson

commit sha accf868130210e8b8b5f985571bc7d2a6a83340c

cmd/mkpkg: add flags for debian scripts.

view details

David Crawshaw

commit sha 304371fdb59bbe67d68d7826711d1ca5482c0a2b

magicsock: spray some normal packets after a handshake In particular, this is designed to catch the case where a HandshakeInitiation packet is sent out but the intermediate NATs have not been primed, so the packet passes over DERP. In that case, the HandshakeResponse also comes back over DERP, and the connection proceeds via DERP without ever trying to punch through the NAT. With this change, the HandshakeResponse (which was sprayed out and so primed one NAT) triggers an UpdateDst, which triggers the extra spray logic. (For this to work, there has to be an initial supply of packets to send on to a peer for the three seconds following a handshake. The source of these packets is left as a future exercise.) Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 2 days

pull request commenttailscale/tailscale

magicsock: spray the next N packets after an endpoint change

I believe this PR now does what it says on the tin.

I've been running morty for a couple days now with this + the wireguard constant handshakes disables. My occasional experiments with doing the same on my mac seems to work. Though there are some unexpected behaviors: the first 1-2 pings from my mac to morty are lost before the connection is established.

I'm going to do some more testing with the the wireguard constant handshakes re-enabled.

crawshaw

comment created time in 2 days

push eventtailscale/tailscale

David Crawshaw

commit sha 868cfae84fa32f5332e53687e99f125a100f54c1

wgengine, magicsock: adjust for wireguard-go conn/device package split Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 2 days

push eventtailscale/wireguard-go

Jonathan Tooker

commit sha f7d0edd2ecf5f7f5aed188823de0a122691a7de6

global: fix a few typos courtesy of codespell Signed-off-by: Jonathan Tooker <jonathan.tooker@netprotect.com>

view details

Jason A. Donenfeld

commit sha 4cdf805b29b1aaca1fab317ca4fce54c7fd69bf6

constants: recalculate rekey max based on a one minute flood Discussed-with: Mathias Hall-Andersen <mathias@hall-andersen.dk>

view details

Jason A. Donenfeld

commit sha 2b242f93932e1c4ab8b45dd0f628dd4fe063699b

wintun: manage ring memory manually It's large and Go's garbage collector doesn't deal with it especially well.

view details

Jason A. Donenfeld

commit sha ddfad453cf22146e9476a5f4b91cf6c47b77e9c7

device: SendmsgN mutates the input sockaddr So we take a new granular lock to prevent concurrent writes from racing. WARNING: DATA RACE Write at 0x00c0011f2740 by goroutine 27: golang.org/x/sys/unix.(*SockaddrInet4).sockaddr() /go/pkg/mod/golang.org/x/sys@v0.0.0-20191105231009-c1f44814a5cd/unix/syscall_linux.go:384 +0x114 golang.org/x/sys/unix.SendmsgN() /go/pkg/mod/golang.org/x/sys@v0.0.0-20191105231009-c1f44814a5cd/unix/syscall_linux.go:1304 +0x288 golang.zx2c4.com/wireguard/device.send4() /go/pkg/mod/golang.zx2c4.com/wireguard@v0.0.20191012/device/conn_linux.go:485 +0x11f golang.zx2c4.com/wireguard/device.(*nativeBind).Send() /go/pkg/mod/golang.zx2c4.com/wireguard@v0.0.20191012/device/conn_linux.go:268 +0x1d6 golang.zx2c4.com/wireguard/device.(*Peer).SendBuffer() /go/pkg/mod/golang.zx2c4.com/wireguard@v0.0.20191012/device/peer.go:151 +0x285 golang.zx2c4.com/wireguard/device.(*Peer).SendHandshakeInitiation() /go/pkg/mod/golang.zx2c4.com/wireguard@v0.0.20191012/device/send.go:163 +0x692 golang.zx2c4.com/wireguard/device.(*Device).RoutineReadFromTUN() /go/pkg/mod/golang.zx2c4.com/wireguard@v0.0.20191012/device/send.go:318 +0x4b8 Previous write at 0x00c0011f2740 by goroutine 386: golang.org/x/sys/unix.(*SockaddrInet4).sockaddr() /go/pkg/mod/golang.org/x/sys@v0.0.0-20191105231009-c1f44814a5cd/unix/syscall_linux.go:384 +0x114 golang.org/x/sys/unix.SendmsgN() /go/pkg/mod/golang.org/x/sys@v0.0.0-20191105231009-c1f44814a5cd/unix/syscall_linux.go:1304 +0x288 golang.zx2c4.com/wireguard/device.send4() /go/pkg/mod/golang.zx2c4.com/wireguard@v0.0.20191012/device/conn_linux.go:485 +0x11f golang.zx2c4.com/wireguard/device.(*nativeBind).Send() /go/pkg/mod/golang.zx2c4.com/wireguard@v0.0.20191012/device/conn_linux.go:268 +0x1d6 golang.zx2c4.com/wireguard/device.(*Peer).SendBuffer() /go/pkg/mod/golang.zx2c4.com/wireguard@v0.0.20191012/device/peer.go:151 +0x285 golang.zx2c4.com/wireguard/device.(*Peer).SendHandshakeInitiation() /go/pkg/mod/golang.zx2c4.com/wireguard@v0.0.20191012/device/send.go:163 +0x692 golang.zx2c4.com/wireguard/device.expiredRetransmitHandshake() /go/pkg/mod/golang.zx2c4.com/wireguard@v0.0.20191012/device/timers.go:110 +0x40c golang.zx2c4.com/wireguard/device.(*Peer).NewTimer.func1() /go/pkg/mod/golang.zx2c4.com/wireguard@v0.0.20191012/device/timers.go:42 +0xd8 Goroutine 27 (running) created at: golang.zx2c4.com/wireguard/device.NewDevice() /go/pkg/mod/golang.zx2c4.com/wireguard@v0.0.20191012/device/device.go:322 +0x5e8 main.main() /go/src/x/main.go:102 +0x58e Goroutine 386 (finished) created at: time.goFunc() /usr/local/go/src/time/sleep.go:168 +0x51 Reported-by: Ben Burkert <ben@benburkert.com>

view details

Jason A. Donenfeld

commit sha 89dd065e53e986234289a0ace66539873be8b075

README: update repo urls

view details

Jason A. Donenfeld

commit sha 4fa2ea6a2dabad50eb5585d0a56c3a6966604fa6

tun: windows: serialize write calls

view details

Jason A. Donenfeld

commit sha caebdfe9d05901230dd7c6b7569be2903fd8ac87

tun: darwin: ignore ENOMEM errors Coauthored-by: Andrej Mihajlov <and@mullvad.net>

view details

Jason A. Donenfeld

commit sha 05b03c675090df893e8317983702c9661dfc319b

version: bump snapshot

view details

Jason A. Donenfeld

commit sha cb4bb63030d09afe7ee78fd49613b86709fdc006

uapi: allow unsetting device private key with /dev/null

view details

Jason A. Donenfeld

commit sha 6ed56ff2dfd57ba47cdf604eb3c455acb553c0df

device: fix private key removal logic

view details

Jason A. Donenfeld

commit sha 9cbcff10dd3e04671d31ab224526f3d22a7ba665

send: account for zero mtu Don't divide by zero.

view details

David Crawshaw

commit sha 55baa3f33bf224b96e51fc8723e5af4c7a5c5c8f

[tempfork] -------- Everything before this commit is already upstream.

view details

David Crawshaw

commit sha e2d18f8526d3a0a71a756ee1250b3142502c4839

rwcancel: fix build on darwin Signed-off-by: David Crawshaw <david@zentus.com>

view details

David Crawshaw

commit sha f6bf6a6ee0c46fcc3a21cc9e62227b0c77438ec1

rwcancel: no-op windows build Signed-off-by: David Crawshaw <david@zentus.com>

view details

David Crawshaw

commit sha 7a5f0920e6ae925dac2d3a977042f7e8da14d7cd

conn: new package that splits out the Bind and Endpoint types The sticky socket code stays in the device package for now, as it reaches deeply into the peer list. Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

David Crawshaw

commit sha 69c9f9bde9d12002836b08cf5ec08f23b04e8b9f

device: have IpcSetOperation return a generic error Signed-off-by: David Crawshaw <david@zentus.com>

view details

Avery Pennarun

commit sha 117b610c764d273b0a9079465ad0603596b49981

namespace_windows: split error message for create vs open namespace. Signed-off-by: Avery Pennarun <apenwarr@gmail.com>

view details

Avery Pennarun

commit sha 12dd7a5637eeafb776e7c2e4d76fb36c29cc484e

device: separate logging for floods and replay attacks Error messages being printed were about floods, but actually what was being detected was a potential "replay attack", ie. a duplicate copy of an incoming handshake. Let's print two separate log messages to differentiate them. Signed-off-by: Avery Pennarun <apenwarr@gmail.com>

view details

David Crawshaw

commit sha f7afa053c1d55e52cd8cce4b5ba8e28a97ad38c3

[tempfork] -------- Everything before this commit is ready to be sent upstream.

view details

Avery Pennarun

commit sha 667cd4493c5b272808c8298c6d86cfb6d0baf5aa

[tempfork] .gitignore

view details

push time in 2 days

startedFiloSottile/age

started time in 2 days

push eventtailscale/tailscale

David Crawshaw

commit sha 8994a59e2011b1b12ec10ecb22f97ae0da9ce3c9

go.mod: update wireguard-go version Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 3 days

push eventtailscale/wireguard-go

Avery Pennarun

commit sha 5ab07c37c8def3af069a5c6092e41be43f1f71dc

device: don't call peer.Stop in unsafeRemovePeer unsafeRemovePeer is called with locks held (and different locks, depending from where it's called). peer.Stop can take an indeterminate amount of time to finish, because it's waiting on goroutines. Those goroutines may be waiting on the same locks, which would cause deadlocks when we got unlucky. Instead, split the "remove peer from list" and "stop the peer" operations into two separate functions, and call the latter without holding any locks. Signed-off-by: Avery Pennarun <apenwarr@gmail.com>

view details

David Crawshaw

commit sha 0567a4cb7c745ddc5dd0ec0b1d80417e6637e7f1

rwcancel: fix build on darwin Signed-off-by: David Crawshaw <david@zentus.com>

view details

David Crawshaw

commit sha e08d6969b082b59130194c704054530595aec38a

[tempfork] -------- Everything before this commit is ready to be sent upstream.

view details

Avery Pennarun

commit sha eeaeac746d0d5cda3f22f492e00cc31709c105a1

[tempfork] .gitignore

view details

David Crawshaw

commit sha 46f8262559adac26355ff668c76849d3bc361388

[tempfork] README warning

view details

David Crawshaw

commit sha 45c4e3901d2de6a908803e0c13656a8f0e1a503a

[tempfork] --------

view details

David Crawshaw

commit sha d9ca53e8018af76a20814bac1aa02a7676884f32

tokenbucket: a token bucket implementation Signed-off-by: David Crawshaw <crawshaw@tailscale.io>

view details

David Crawshaw

commit sha 50d9dd101d08f212cd3eecda29f226959513ccc4

device: use tokenbucket package for handshake initiation A second fast handshake initiation is immediately rejected by the current flood control algorithm. This commit uses a token bucket, allowing a small burst of handshake initiation packets before dropping them. Signed-off-by: David Crawshaw <crawshaw@tailscale.io>

view details

David Crawshaw

commit sha 1d383cfeb6f05de41e5ba2bb63a587c43269a723

device: add a test that demonstrates handshakes racing Each side attempts to establish a tunnel at the same time. The result is the handshakes cross in-flight, both sides get confused, and we have to wait for the multi-second timeout to get a tunnel established. Without a handshake race, it is possible to set const maxWait = 300 * time.Millisecond to get the test passing reliably even on slow machines. Signed-off-by: David Crawshaw <crawshaw@tailscale.io>

view details

Avery Pennarun

commit sha 5970025b46cb5d1b9cdbc3a52a79371ddca98261

device: separate replay attack protection from flood protection Error messages being printed were about floods, but actually what was being detected was a potential "replay attack", ie. a duplicate copy of an incoming handshake. Let's print two separate log messages to differentiate them. Signed-off-by: Avery Pennarun <apenwarr@gmail.com>

view details

David Crawshaw

commit sha 00313ecb780a7824da1d9e7e40fde3c872e9330c

device: use SO_REUSEADDR for macOS and windows Looks like conn_linux.go already does this, and testing with a STUN server on the same port agrees, which is nice. This package being imported is far too large for what is 5 lines of code for each OS, but I'll leave the cleanup for a later commit.

view details

Avery Pennarun

commit sha f7fe2b0d6b958b3f808bbb6df5398623fd5c635b

Don't delay initial handshakes right after creating a peer. Initializing a peer would default the lastSentHandshake time to ~1 second in the future, preventing any initial handshake from triggering until a retry period had passed. This seems counterproductive, so I removed that. Now we connect extremely fast after a reconfig. But I don't know why this was here, so maybe it's risky to remove.

view details

Avery Pennarun

commit sha 2e7182c9c7b2b01b37b5bf616c096e89a5751e83

device: add a Stats object

view details

David Crawshaw

commit sha 984316799515887a2d36de2de69a0caf09d5f34a

device: have IpcSetOperation return a generic error

view details

David Crawshaw

commit sha 3f933bb8de8c82045098c9ca117b9cdc9aee3b40

wgconf: import types and config parser from wireguard-windows

view details

Avery Pennarun

commit sha 395496c13c81e95c2b90df4b431d79d0d22f0643

device: add a callback when an unexpected IP is used by a peer

view details

David Crawshaw

commit sha 2f87b275a988f51bcb0f31dd9a7a7235539370de

device: allow config of CreateBind/CreateEndpoint We also store public keys of peers in endpoints. This lets us send messages to a peer using the key as an address.

view details

Avery Pennarun

commit sha 09bbfc6088a8ab10c5d59e9f09455900fe140699

device: add a HandshakeDone() callback. Every time a peer handshake completes, we call this function. That lets a GUI immediately notice when the connection has been established.

view details

Avery Pennarun

commit sha dea2e1759f271cb5b371e09814f9b97d5e052344

device: Simple packet filtering support. An app can inject a FilterPacket() function that can decide whether to accept or reject a given packet based on its contents. The default is the old behaviour. It runs right after decrypt, in the same goroutine, in order to take advantage of the same parallelism. Beware that this means your FilterPacket() function might be run many times in parallel (with different buffers), so beware that it needs to be reentrant. TODO: this adds a device.SetFilterPacket() function. I want to be able to update the packet filter without restarting wireguard. This is a bit ugly because it introduces a lock into a high-throughput pathway, which means the final version of this feature probably can't really work like this.

view details

Avery Pennarun

commit sha 8947289d38a538ad0fe34b143660f0cb6e7308f3

[Tailscale] HACKS: wintun: faster discovery, skip interface create/delete. Remove wintun "network name" check. We don't need this for tailscale because we only need one network interface total. The end user might rename the network interface, so this is not a safe string to use for finding interfaces anyway. Don't ever create new wintun interfaces. If we can't find the default one, just fail. Creating interfaces requires admin access, which we don't want to be necessary.

view details

push time in 3 days

push eventtailscale/tailscale

David Crawshaw

commit sha 74e34c2399e5604d7a5c0ed1e3a2be62be038811

magicsock: spray the next N packets after an endpoint change In particular, this is designed to catch the case where a HandshakeInitiation packet is sent out but the intermediate NATs have not been primed, so the packet passes over DERP. In that case, the HandshakeResponse also comes back over DERP, and the connection proceeds via DERP without ever trying to punch through the NAT. With this change, the HandshakeResponse (which was sprayed out and so primed one NAT) triggers an UpdateDst, which marks the next 5 non-handshake packets for spraying. At this point both NATs are primed, one of these 5 packets will make it through, triggering an UpdateDst on the other side and then both sides will be talking directly without DERP. Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 4 days

pull request commenttailscale/tailscale

magicsock: spray the next N packets after an endpoint change

OK reasonably sure this doesn't work as intended. Digging.

crawshaw

comment created time in 4 days

PR opened tailscale/tailscale

controlclient: disable keepalives when using DERP

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

+13 -1

0 comment

1 changed file

pr created time in 4 days

create barnchtailscale/tailscale

branch : crawshaw/derp-nokeepalives

created branch time in 4 days

issue commenttailscale/tailscale

Magicsock refuses to downgrade from LAN to DERP

I like that, you can condition on just outbound handshake{init,response}. Very lightweight.

danderson

comment created time in 4 days

pull request commenttailscale/tailscale

WIP magicsock: spray the next N packets after an endpoint change

Some initial testing: I upgraded morty to tailscaled and turned on DERP.

Then I modified the iOS client to set DEBUG_FORCE_DERP. When pinging from my mac to morty locally, I see a ping long enough to be bounced through derp, then a ping fast enough that it must have upgraded to the LAN. Then the macOS client crashed. :-/

I haven't been able to replicate the crash reliably yet (it was lacking in obvious logs, suggesting it might be a memory usage issue). Right now my macOS client is stable.

Pings are a little erratic in a way I don't understand yet, but my connections seem to be stable and unless the DERP server is 1.5ms from my house, there's no way my pings between laptop and morty are moving via DERP.

crawshaw

comment created time in 4 days

issue openedtailscale/tailscale

tailscale.deb does not print a note about how to start the service

I ran:

sudo dpkg -i ./out/x86_64-linux/packages/tailscale.deb

...on Ubuntu. It installed everything, but did not print the systemd magic commands to install and start the service, as many other packages do. (You probably don't forget them as much as I do, the pointers are really helpful for me.)

created time in 4 days

issue commenttailscale/tailscale

Magicsock refuses to downgrade from LAN to DERP

FWIW I don't think this failure to downgrade to DERP should block us rolling out DERP, because it's still strictly better than what we have today.

As to this particular bug, one workaround would be an AddrSet keeping a lastSeen time.Time for the current priority, and if X seconds have passed since then, reset the priority to nothing.

danderson

comment created time in 4 days

push eventtailscale/tailscale

David Crawshaw

commit sha 9b6e7b9648d7f6925b1d30156a06628f107f93f0

magicsock: spray the next N packets after an endpoint change In particular, this is designed to catch the case where a HandshakeInitiation packet is sent out but the intermediate NATs have not been primed, so the packet passes over DERP. In that case, the HandshakeResponse also comes back over DERP, and the connection proceeds via DERP without ever trying to punch through the NAT. With this change, the HandshakeResponse (which was sprayed out and so primed one NAT) triggers an UpdateDst, which marks the next 5 non-handshake packets for spraying. At this point both NATs are primed, one of these 5 packets will make it through, triggering an UpdateDst on the other side and then both sides will be talking directly without DERP. Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 4 days

PR opened tailscale/tailscale

Reviewers
WIP magicsock: spray the next N packets after an endpoint change

[WIP: still trying to test locally]

Intended to help with upgrading from DERP to direct UDP connections.

In particular, this is designed to catch the case where a HandshakeInitiation packet is sent out but the intermediate NATs have not been primed, so the packet passes over DERP. In that case, the HandshakeResponse also comes back over DERP, and the connection proceeds via DERP without ever trying to punch through the NAT.

With this change, the HandshakeResponse (which was sprayed out and so primed one NAT) triggers an UpdateDst, which marks the next 5 non-handshake packets for spraying. At this point both NATs are primed, one of these 5 packets will make it through, triggering an UpdateDst on the other side and then both sides will be talking directly without DERP.

+11 -0

0 comment

1 changed file

pr created time in 4 days

create barnchtailscale/tailscale

branch : crawshaw/spray

created branch time in 4 days

push eventtailscale/tailscale

David Crawshaw

commit sha ebc70acac7bf620a8b37d70d7a70cd2181fea2ce

go.mod: update wireguard-go version

view details

push time in 4 days

push eventtailscale/wireguard-go

David Crawshaw

commit sha 8cb4c88c1deba88d7a74288d1606af856f8c35d0

[Tailscale] device: update endpoint even on replayed packets If the sender used multiple channels, let the Endpoint know about every address we see.

view details

push time in 4 days

issue commenttailscale/tailscale

panic: Tried to generate emptyPrivateKey.Public()

Thanks for the bug report. Do you happen to have the full panic message with all the goroutine stacks?

simonlbn

comment created time in 5 days

Pull request review commenttailscale/tailscale

derp: change the protocol framing to always include a length

 func (s *Server) verifyClient(clientKey key.Public, info *sclientInfo) error { }  func (s *Server) sendServerKey(bw *bufio.Writer) error {-	if err := putUint32(bw, magic); err != nil {-		return err-	}-	if err := typeServerKey.Write(bw); err != nil {-		return err+	buf := make([]byte, 0, 40)+	buf = append(buf, magic...)+	buf = append(buf, s.publicKey[:]...)+	if len(buf) != 40 {+		panic("")

msg?

bradfitz

comment created time in 6 days

Pull request review commenttailscale/tailscale

derp: change the protocol framing to always include a length

 func (c *Client) recvServerInfo() (*serverInfo, error) { }  func (c *Client) sendClientKey() error {-	var nonce [24]byte+	var nonce [nonceLen]byte 	if _, err := crand.Read(nonce[:]); err != nil { 		return err 	} 	msg := []byte("{}") // no clientInfo for now 	msgbox := box.Seal(nil, msg, &nonce, c.serverKey.B32(), c.privateKey.B32()) -	if _, err := c.bw.Write(c.publicKey[:]); err != nil {-		return err-	}-	if _, err := c.bw.Write(nonce[:]); err != nil {-		return err-	}-	if err := putUint32(c.bw, uint32(len(msgbox))); err != nil {-		return err-	}-	if _, err := c.bw.Write(msgbox); err != nil {-		return err-	}-	return c.bw.Flush()+	buf := make([]byte, 0, nonceLen+keyLen+len(msgbox))+	buf = append(buf, c.publicKey[:]...)+	buf = append(buf, nonce[:]...)+	buf = append(buf, msgbox...)+	return writeFrame(c.bw, frameClientInfo, buf) } -func (c *Client) Send(dstKey key.Public, msg []byte) (err error) {+func (c *Client) Send(dstKey key.Public, msg []byte) error { return c.send(dstKey, msg) }++func (c *Client) send(dstKey key.Public, msg []byte) (ret error) { 	defer func() {-		if err != nil {-			err = fmt.Errorf("derp.Send: %v", err)+		if ret != nil {+			ret = fmt.Errorf("derp.Send: %v", ret) 		} 	}() -	if err := typeSendPacket.Write(c.bw); err != nil {-		return err+	// Verify len(msg) + 4 fits in a uint32 with a sanity check.+	// This isn't a real limit, but is much higher than a IP+	// packet size.+	if len(msg) > 10<<20 {

This could be a lot smaller right? We move UDP packets, so the limit is 64kb. 1<<17?

bradfitz

comment created time in 6 days

issue commenttailscale/tailscale

Lock down systemd unit configuration more

I guess we could strace ip and iptables to work out what netlink they are doing. :-)

danderson

comment created time in 6 days

push eventtailscale/tailscale

David Crawshaw

commit sha 4ebc0fa70f0994493c3289852701d9b671014d9e

wgengine: incremental update of peers on network map change This is the first, and easier, part of incremental wireguard-go reconfiguration. It means that a new node appearing on the network does not cause all existing nodes to re-handshake with the other nodes they are talking to. (This code has been running on hello.ipn.dev for a few weeks and peers have successfully reconnected to it through many network map updates.) Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 6 days

Pull request review commenttailscale/tailscale

wgengine/magicsock: support multiple derp servers, and use for all data

 func (c *Conn) LocalPort() uint16 { 	return uint16(laddr.Port) } -func (c *Conn) Send(b []byte, ep device.Endpoint) error {-	a := ep.(*AddrSet)-+func shouldSprayPacket(b []byte) bool {+	if len(b) < 4 {+		return false+	} 	msgType := binary.LittleEndian.Uint32(b[:4]) 	switch msgType {-	case device.MessageInitiationType, device.MessageResponseType, device.MessageCookieReplyType:-		// Part of the wireguard handshake.-		// Send to every potential endpoint we have for a peer.-		a.mu.Lock()-		roamAddr := a.roamAddr-		a.mu.Unlock()--		var err error-		var success bool-		if roamAddr != nil {-			_, err = c.pconn.WriteTo(b, roamAddr)-			if err == nil {-				success = true-			}+	case device.MessageInitiationType,+		device.MessageResponseType,+		device.MessageCookieReplyType: // TODO: necessary?+		return true+	}+	return false+}++// packetDsts returns the destinations that b should be written to in+// order to reach as. Some of the returned UDPAddrs may be fake addrs+// representing DERP servers.+//+// If oneDst is non-nil, dsts should be ignored. The oneDsts is only+// returned if there's exactly 1 destination, as there is in the common+// fast path. This avoids an allocation.+//+// It also returns as's current roamAddr, if any.+func packetDsts(as *AddrSet, b []byte) (oneDst *net.UDPAddr, dsts []*net.UDPAddr, roamAddr *net.UDPAddr) {+	spray := shouldSprayPacket(b)++	as.mu.Lock()+	defer as.mu.Unlock()++	roamAddr = as.roamAddr+	if roamAddr != nil {+		if !spray {+			return roamAddr, nil, roamAddr 		}-		for i := len(a.addrs) - 1; i >= 0; i-- {-			addr := &a.addrs[i]-			_, err = c.pconn.WriteTo(b, addr)-			if err == nil {-				success = true-			}+		dsts = append(dsts, roamAddr)+	}+	for i := len(as.addrs) - 1; i >= 0; i-- {+		addr := &as.addrs[i]+		if !spray && as.curAddr == i {+			return addr, nil, nil 		}+		if spray || as.curAddr == -1 || as.curAddr != i {+			dsts = append(dsts, addr)+		}+	} -		if msgType == device.MessageInitiationType {-			// Send initial handshake messages via DERP.-			c.derpMu.Lock()-			derp := c.derp-			c.derpMu.Unlock()+	return nil, dsts, roamAddr+} -			if derp != nil {-				if err := derp.Send(a.publicKey, b); err != nil {-					log.Printf("derp send failed: %v", err)-				}+var errNoDestinations = errors.New("magicsock: no destinations")++func (c *Conn) Send(b []byte, ep device.Endpoint) error {+	as := ep.(*AddrSet)+	oneDst, dsts, roamAddr := packetDsts(as, b)+	if oneDst != nil {+		// Common case.+		return c.sendAddr(oneDst, as.publicKey, b)+	}++	if len(dsts) == 0 {+		return errNoDestinations+	}++	var success bool+	var ret error+	for _, addr := range dsts {+		err := c.sendAddr(addr, as.publicKey, b)+		if err == nil {+			success = true+		} else if ret == nil {+			ret = err+		}+		if err != nil && addr != roamAddr {+			log.Printf("magicsock: Conn.Send(%v): %v", addr, err)+		}+	}+	if success {+		return nil+	}+	return ret+}++var errConnClosed = errors.New("Conn closed")++var errDropDerpPacket = errors.New("too many DERP packets queued; dropping")++// sendAddr sends packet b to addr, which is either a real UDP address+// or a fake UDP address representing a DERP server (see derpmap.go).+// The provided public key identifies the recipient.+func (c *Conn) sendAddr(addr *net.UDPAddr, pubKey key.Public, b []byte) error {+	if ch := c.derpWriteChanOfAddr(addr); ch != nil {+		errc := make(chan error, 1)+		select {+		case <-c.donec:+			return errConnClosed+		case ch <- derpWriteRequest{addr, pubKey, b, errc}:+			select {+			case <-c.donec:+				return errConnClosed+			case err := <-errc:+				return err // usually nil 			}+		default:+			// Too many writes queued. Drop packet.+			return errDropDerpPacket 		}+	}+	_, err := c.pconn.WriteTo(b, addr)+	return err+} -		if success {+// bufferedDerpWritesBeforeDrop is how many packets writes can be+// queued up the DERP client to write on the wire before we start+// dropping.+//+// TODO: this is currently arbitrary. Figure out something better?+const bufferedDerpWritesBeforeDrop = 4++// derpWriteChanOfAddr returns a DERP client for fake UDP addresses that+// represent DERP servers, creating them as necessary. For real UDP+// addresses, it returns nil.+func (c *Conn) derpWriteChanOfAddr(addr *net.UDPAddr) chan<- derpWriteRequest {+	if !addr.IP.Equal(derpMagicIP) {+		return nil+	}+	c.derpMu.Lock()+	defer c.derpMu.Unlock()+	ch, ok := c.derpWriteCh[addr.Port]+	if !ok {+		if c.derpWriteCh == nil {+			c.derpWriteCh = make(map[int]chan<- derpWriteRequest)+			c.derpConn = make(map[int]*derphttp.Client)+		}+		host := derpHost(addr.Port)+		dc, err := derphttp.NewClient(c.privateKey, "https://"+host+"/derp", log.Printf)+		if err != nil {+			log.Printf("derphttp.NewClient: port %d, host %q invalid? err: %v", addr.Port, host, err) 			return nil 		}++		bidiCh := make(chan derpWriteRequest, bufferedDerpWritesBeforeDrop)+		ch = bidiCh+		c.derpConn[addr.Port] = dc+		c.derpWriteCh[addr.Port] = ch+		go c.runDerpReader(addr, dc)+		go c.runDerpWriter(addr, dc, bidiCh) 	}+	return ch+} -	// Write to the highest-priority address we have seen so far.-	_, err := c.pconn.WriteTo(b, a.dst())-	return err+// derpReadResult is the type sent by runDerpClient to ReceiveIPv4+// when a DERP packet is available.+type derpReadResult struct {+	derpAddr *net.UDPAddr+	n        int // length of data received++	// copyBuf is called to copy the data to dst.  It returns how+	// much data was copied, which will be n if dst is large+	// enough.+	copyBuf func(dst []byte) int+}++// runDerpReader runs in a goroutine for the life of a DERP+// connection, handling received packets.+func (c *Conn) runDerpReader(derpFakeAddr *net.UDPAddr, dc *derphttp.Client) {+	var buf [64 << 10]byte+	didCopy := make(chan struct{}, 1)+	for {+		n, err := dc.Recv(buf[:])+		if err != nil {+			if err == derphttp.ErrClientClosed {+				return+			}+			select {+			case <-c.donec:+				return+			default:+			}+			log.Printf("derp.Recv: %v", err)+			time.Sleep(250 * time.Millisecond)+			continue+		}+		copyFn := func(dst []byte) int {

Not important, just curious: does copyFn get hoisted, or does it get allocated on each loop?

bradfitz

comment created time in 6 days

Pull request review commenttailscale/tailscale

wgengine/magicsock: support multiple derp servers, and use for all data

 func (c *Conn) LocalPort() uint16 { 	return uint16(laddr.Port) } -func (c *Conn) Send(b []byte, ep device.Endpoint) error {-	a := ep.(*AddrSet)-+func shouldSprayPacket(b []byte) bool {+	if len(b) < 4 {+		return false+	} 	msgType := binary.LittleEndian.Uint32(b[:4]) 	switch msgType {-	case device.MessageInitiationType, device.MessageResponseType, device.MessageCookieReplyType:-		// Part of the wireguard handshake.-		// Send to every potential endpoint we have for a peer.-		a.mu.Lock()-		roamAddr := a.roamAddr-		a.mu.Unlock()--		var err error-		var success bool-		if roamAddr != nil {-			_, err = c.pconn.WriteTo(b, roamAddr)-			if err == nil {-				success = true-			}+	case device.MessageInitiationType,+		device.MessageResponseType,+		device.MessageCookieReplyType: // TODO: necessary?+		return true+	}+	return false+}++// packetDsts returns the destinations that b should be written to in+// order to reach as. Some of the returned UDPAddrs may be fake addrs+// representing DERP servers.+//+// If oneDst is non-nil, dsts should be ignored. The oneDsts is only+// returned if there's exactly 1 destination, as there is in the common+// fast path. This avoids an allocation.

Might be able to avoid all allocations with appendPacketDsts(as *AddrSet, b []byte, dsts []*net.UDPAddr) []*net.UDPAddr.

The Send method should be able to keep a slice that it passes on the stack.

bradfitz

comment created time in 7 days

push eventtailscale/tailscale

David Crawshaw

commit sha 45d687e213216c1590128fc7556d77adfcce179e

wgengine: fix build on linux/freebsd/openbsd/windows Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 7 days

push eventtailscale/tailscale

David Crawshaw

commit sha 431929c09a93779d2fcb04fbc65684291a7dcf52

go.mod: fix wireguard-go version

view details

push time in 7 days

push eventtailscale/tailscale

David Crawshaw

commit sha d0f697ee07eeee7268126ea1fb495ab55e864d19

wgengine: update for wgcfg changes Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 7 days

push eventtailscale/wireguard-go

David Crawshaw

commit sha 9b246dcd5af71045d7df1d89ba765a8b5a0c1fe7

rwcancel: fix build on darwin Signed-off-by: David Crawshaw <david@zentus.com>

view details

David Crawshaw

commit sha 0f74fd30d33cb2817cc742344f4ae243e69da20f

wgcfg: remove unnecessary Interface intermediate type While here, rename Dns and Mtu to DNS and MTU.

view details

David Crawshaw

commit sha ff5ce9e55c864efc89dd5c62bb901a83fa214a0f

wgcfg: add Config.Copy method

view details

push time in 7 days

pull request commenttailscale/tailscale

wgengine/magicsock: support multiple derp servers, and use for all data

General shape looks like we discussed. LMK when you want a review.

bradfitz

comment created time in 8 days

push eventtailscale/tailscale

David Crawshaw

commit sha a23a0d9c9f0ce6c37a7322c39ab52b8dd845d900

tailcfg: add RegisterRequest.Copy Add some docs while I'm here. Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 8 days

Pull request review commenttailscale/tailscale

derp: use new types/key package

+// 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 key defines some types related to curve25519 keys.+package key++import "golang.org/x/crypto/curve25519"++// Private represents a curve25519 private key.+type Private [32]byte++// B32 returns k as the *[32]byte type that's used by the+// golang.org/x/crypto packages.+func (k *Private) B32() *[32]byte { return (*[32]byte)(k) }

This aliases the memory of k. Do we want that? My instinct is to copy these tiny 4-register fields where possible to reduce the likelihood of something funny happening.

bradfitz

comment created time in 9 days

issue commenttailscale/tailscale

tailscale-login will not authenicate or recognise URL visit

After visiting the URL and authenticating, do you see a "Authentication Successful" page?

glennrice

comment created time in 12 days

Pull request review commenttailscale/tailscale

logpolicy: add some docs

 func (c *Config) ToBytes() []byte { 	return data } -func (c *Config) Save(statefile string) {+// Save writes the JSON representation of c to stateFile.+func (c *Config) Save(stateFile string) error {

Do you have a separate PR to use this error so it isn't lost?

bradfitz

comment created time in 13 days

create barnchtailscale/wireguard-go

branch : master

created branch time in 13 days

created repositorytailscale/wireguard-go

Temporary fork of https://git.zx2c4.com/wireguard-go

created time in 13 days

push eventtailscale/tailscale

David Crawshaw

commit sha 2b947b3b4012d6f0c35000c81159c5ff060478d1

controlclient: handle nil Logf option

view details

David Crawshaw

commit sha cbfef0c8b7e0925f7ae5016f9421d3bb65cb89c3

Merge branch 'master' of github.com:tailscale/tailscale into HEAD

view details

David Crawshaw

commit sha 3bf9d53d404968dc978e181929e0ee094d346abc

Merge branch 'master' of github.com:tailscale/tailscale into HEAD

view details

David Crawshaw

commit sha aa73b7972c4b0275827304c42a4de3127d100ea3

Merge branch 'master' of github.com:tailscale/tailscale into HEAD

view details

push time in 13 days

push eventtailscale/tailscale

David Crawshaw

commit sha a6314665876ad8fc1bf6fa5133dba4e07008fbe2

cmd/tsshd: empty file for windows build Signed-off-by: David Crawshaw <david@zentus.com>

view details

push time in 13 days

issue commenttailscale/tailscale

do STUN lookup periodically

@apenwarr derp has an explicit heartbeat (though only in one direction right now iirc, it will need one the other way too).

bradfitz

comment created time in 13 days

issue commenttailscale/tailscale

do STUN lookup periodically

I think taking our cue from DERP connection failures might actually cover all needs to STUN again, without a timer. \o/

(Though I think in the current DERP code, keepalives only flow in one direction, as discussed earlier, they need to be bi-directional for reliability.)

bradfitz

comment created time in 14 days

issue commenttailscale/tailscale

Fails to start on Linux if wg0 already exists

We should definitely name it something other than wg0. tailscale0 works for me.

paulsmith

comment created time in 14 days

startedgliderlabs/ssh

started time in 14 days

pull request commenttailscale/tailscale

Add OpenBSD support

Leaving for @danderson to review.

martinbaillie

comment created time in 14 days

Pull request review commenttailscale/tailscale

Add OpenBSD support

+// 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 (+	"fmt"+	"log"+	"net"+	"os/exec"++	"github.com/tailscale/wireguard-go/device"+	"github.com/tailscale/wireguard-go/tun"+	"github.com/tailscale/wireguard-go/wgcfg"+	"tailscale.com/logger"+)++// For now this router only supports the userspace WireGuard implementations.+//+// There is an experimental kernel version in the works:+// https://git.zx2c4.com/wireguard-openbsd.+//+// TODO(mbaillie): netlink-style monitoring might be possible through+// `ifstated(8)`/`devd(8)`, or become possible with the OpenBSD kernel+// implementation. This merits further investigation.++type openbsdRouter struct {+	logf    func(fmt string, args ...interface{})+	tunname string+	local   wgcfg.CIDR+	routes  map[wgcfg.CIDR]struct{}+}++func NewUserspaceRouter(logf logger.Logf, tunname string, _ *device.Device, tuntap tun.Device, _ func()) Router {+	r := openbsdRouter{+		logf:    logf,+		tunname: tunname,+	}+	return &r+}++// TODO(mbaillie): extract as identical to linux version+func cmd(args ...string) *exec.Cmd {+	if len(args) == 0 {+		log.Fatalf("exec.Cmd(%#v) invalid; need argv[0]\n", args)+	}+	return exec.Command(args[0], args[1:]...)+}++func (r *openbsdRouter) Up() error {+	// TODO(mbaillie): MTU set elsewhere?++	ifup := []string{"ifconfig", r.tunname, "up"}+	if out, err := cmd(ifup...).CombinedOutput(); err != nil {+		r.logf("running ifconfig failed: %v\n%s", err, out)+		return err+	}+	return nil+}++func (r *openbsdRouter) SetRoutes(rs RouteSettings) error {+	var errq error++	if rs.LocalAddr != r.local {+		if r.local != (wgcfg.CIDR{}) {+			addrdel := []string{"ifconfig", r.tunname,+				"inet", r.local.String(), "-alias"}+			out, err := cmd(addrdel...).CombinedOutput()+			if err != nil {+				r.logf("addr del failed: %v: %v\n%s", addrdel, err, out)+				if errq == nil {+					errq = err+				}+			}++			routedel := []string{"route", "-q", "-n",+				"del", "-inet", r.local.String(),+				"-iface", r.local.IP.String()}+			if out, err := cmd(routedel...).CombinedOutput(); err != nil {+				r.logf("route del failed: %v: %v\n%s", routedel, err, out)+				if errq == nil {+					errq = err+				}+			}+		}++		addradd := []string{"ifconfig", r.tunname,+			"inet", rs.LocalAddr.String(), "alias"}+		out, err := cmd(addradd...).CombinedOutput()+		if err != nil {+			r.logf("addr add failed: %v: %v\n%s", addradd, err, out)+			if errq == nil {+				errq = err+			}+		}++		routeadd := []string{"route", "-q", "-n",+			"add", "-inet", rs.LocalAddr.String(),+			"-iface", rs.LocalAddr.IP.String()}+		if out, err := cmd(routeadd...).CombinedOutput(); err != nil {+			r.logf("route add failed: %v: %v\n%s", routeadd, err, out)+			if errq == nil {+				errq = err+			}+		}+	}++	newRoutes := make(map[wgcfg.CIDR]struct{})+	for _, peer := range rs.Cfg.Peers {+		for _, route := range peer.AllowedIPs {+			newRoutes[route] = struct{}{}+		}+	}+	for route := range r.routes {+		if _, keep := newRoutes[route]; !keep {+			net := route.IPNet()+			nip := net.IP.Mask(net.Mask)+			nstr := fmt.Sprintf("%v/%d", nip, route.Mask)+			routedel := []string{"route", "-q", "-n",+				"del", "-inet", nstr,+				"-iface", rs.LocalAddr.IP.String()}+			out, err := cmd(routedel...).CombinedOutput()+			if err != nil {+				r.logf("route del failed: %v: %v\n%s", routedel, err, out)+				if errq == nil {+					errq = err+				}+			}+		}+	}+	for route := range newRoutes {+		if _, exists := r.routes[route]; !exists {+			net := route.IPNet()+			nip := net.IP.Mask(net.Mask)+			nstr := fmt.Sprintf("%v/%d", nip, route.Mask)+			routeadd := []string{"route", "-q", "-n",+				"add", "-inet", nstr,+				"-iface", rs.LocalAddr.IP.String()}+			out, err := cmd(routeadd...).CombinedOutput()+			if err != nil {+				r.logf("addr add failed: %v: %v\n%s", routeadd, err, out)+				if errq == nil {+					errq = err+				}+			}+		}+	}++	r.local = rs.LocalAddr+	r.routes = newRoutes++	if err := r.replaceResolvConf(rs.DNS, rs.DNSDomains); err != nil {+		errq = fmt.Errorf("replacing resolv.conf failed: %v", err)+	}++	return errq+}++func (r *openbsdRouter) Close() {+	out, err := cmd("ifconfig", r.tunname, "down").CombinedOutput()+	if err != nil {+		r.logf("running ifconfig failed: %v\n%s", err, out)+	}++	if err := r.restoreResolvConf(); err != nil {+		r.logf("failed to restore system resolv.conf: %v", err)+	}++	// TODO(mbaillie): wipe routes+}++// TODO(mbaillie): these are no-ops for now. They could re-use the Linux funcs+// (sans systemd parts), but I note Linux DNS is disabled(?) so leaving for now.

We disabled it because there's no user option to disable it. @apenwarr didn't want installing relaynode to mean you must accept your resolv.conf being overwritten.

From my reading of the systemd docs, as amazing as this is: if you overwrite /etc/resolv.conf, systemd-resolved will respect it. So I believe this is moderately portable.

martinbaillie

comment created time in 14 days

issue commenttailscale/tailscale

do STUN lookup periodically

Is there anything else we can do here but a timer?

If we are maintaining an active TCP session to DERP, could the DERP server inform us if our remote-addr changes? (Will it even change, or will the TCP connection reset? If so, that would be easy to detect.)

bradfitz

comment created time in 14 days

Pull request review commenttailscale/tailscale

wgengine: flesh out some docs

 func (rs *RouteSettings) OnlyRelevantParts() string { 		rs.LocalAddr, rs.DNS, rs.DNSDomains, peers) } +// Router is the TODO. type Router interface {+	// Up brings the router up.+	// TODO: more than once? after Close? 	Up() error-	SetRoutes(rs RouteSettings) error+	// SetRoutes sets the routes.+	// TODO: while running?

This is called on network map updates, so periodically. It's how you get an entry in your kernel route table for a peer.

bradfitz

comment created time in 14 days

Pull request review commenttailscale/tailscale

wgengine: flesh out some docs

 func (rs *RouteSettings) OnlyRelevantParts() string { 		rs.LocalAddr, rs.DNS, rs.DNSDomains, peers) } +// Router is the TODO. type Router interface {+	// Up brings the router up.+	// TODO: more than once? after Close? 	Up() error-	SetRoutes(rs RouteSettings) error+	// SetRoutes sets the routes.+	// TODO: while running?+	SetRoutes(RouteSettings) error+	// Close closes the router.+	// TODO: return an error? does this block?

It does not block on network I/O. (I don't think it works properly at all, but if it did it still wouldn't block.) Returning an error is probably a good idea.

bradfitz

comment created time in 14 days

Pull request review commenttailscale/tailscale

wgengine: flesh out some docs

 func (rs *RouteSettings) OnlyRelevantParts() string { 		rs.LocalAddr, rs.DNS, rs.DNSDomains, peers) } +// Router is the TODO. type Router interface {+	// Up brings the router up.+	// TODO: more than once? after Close?

Once. It might be possible to move this code into the New function that creates the router, but I'd have to dig through the initialization order to be sure.

I hate to think what happens after Close. let the object go.

bradfitz

comment created time in 14 days

Pull request review commenttailscale/tailscale

wgengine: flesh out some docs

 type PeerStatus struct { 	NodeKey          tailcfg.NodeKey } +// Status is the Engine status. type Status struct { 	Peers      []PeerStatus 	LocalAddrs []string // TODO(crawshaw): []wgcfg.Endpoint? } -type StatusCallback func(s *Status, err error)+// StatusCallback is the type of status callbacks used by+// Engine.SetStatusCallback.+//+// Exactly one of Status or error is non-nil.+type StatusCallback func(*Status, error) +// RouteSettings is the full WireGuard config data (set of peers keys,+// IP, etc in wgcfg.Config) plus the things that WireGuard doesn't do+// itself, like DNS stuff. type RouteSettings struct {-	LocalAddr  wgcfg.CIDR+	LocalAddr  wgcfg.CIDR // TODO: why is this here? how does it differ from wgcfg.Config's info?

On closer inspection, I don't get it. This looks like an oversimplification in the router logic. Implementations of SetRoutes should be using the set of localaddrs in Cfg.Interface.Addresses. (It's a fine simplification right now because we give every node a single cgnat address, but we want the option to do more than one later, e.g. ipv6.)

So I think this needs a proper TODO in the code to replace with Cfg.Interface.Addresses.

bradfitz

comment created time in 14 days

Pull request review commenttailscale/tailscale

wgengine: flesh out some docs

 func (rs *RouteSettings) OnlyRelevantParts() string { 		rs.LocalAddr, rs.DNS, rs.DNSDomains, peers) } +// Router is the TODO.

Hmm. Router is a <something> that is responsible for managing the system route table.

There's only one instance, and one per-OS implementation. So you could say the interface is a little bit unnecessary. But it's a nice place to hang docs.

bradfitz

comment created time in 14 days

Pull request review commenttailscale/tailscale

wgengine: flesh out some docs

 type PeerStatus struct { 	NodeKey          tailcfg.NodeKey } +// Status is the Engine status. type Status struct { 	Peers      []PeerStatus 	LocalAddrs []string // TODO(crawshaw): []wgcfg.Endpoint? } -type StatusCallback func(s *Status, err error)+// StatusCallback is the type of status callbacks used by+// Engine.SetStatusCallback.+//+// Exactly one of Status or error is non-nil.+type StatusCallback func(*Status, error) +// RouteSettings is the full WireGuard config data (set of peers keys,+// IP, etc in wgcfg.Config) plus the things that WireGuard doesn't do+// itself, like DNS stuff. type RouteSettings struct {-	LocalAddr  wgcfg.CIDR+	LocalAddr  wgcfg.CIDR // TODO: why is this here? how does it differ from wgcfg.Config's info? 	DNS        []net.IP 	DNSDomains []string-	Cfg        wgcfg.Config+	Cfg        wgcfg.Config // TODO: value type here, but pointer below?

I suppose this should be a pointer. The data structure contains pointers, so the value type does not make a copy of it.

bradfitz

comment created time in 14 days

Pull request review commenttailscale/tailscale

wgengine: flesh out some docs

 import ( 	"tailscale.com/wgengine/filter" ) +// ByteCount is the number of bytes that have been sent or received.+// TODO: why is this a type? for a stringer? where's the stringer?

This status information from wireguard is half-baked. I don't think this or the next TODO has a good answer. (Or if it does: I don't know what it is.)

bradfitz

comment created time in 15 days

Pull request review commenttailscale/tailscale

wgengine: flesh out some docs

 func (rs *RouteSettings) OnlyRelevantParts() string { 		rs.LocalAddr, rs.DNS, rs.DNSDomains, peers) } +// Router is the TODO. type Router interface {+	// Up brings the router up.+	// TODO: more than once? after Close? 	Up() error-	SetRoutes(rs RouteSettings) error+	// SetRoutes sets the routes.+	// TODO: while running?+	SetRoutes(RouteSettings) error+	// Close closes the router.+	// TODO: return an error? does this block? 	Close() } +// Engine is the Tailscale WireGuard engine interface. type Engine interface {-	// Reconfigure wireguard and make sure it's running.+	// Reconfig reconfigures WireGuard and makes sure it's running. 	// This also handles setting up any kernel routes.+	// TODO: why is dnsDomains not in cfg itself?+	// TODO: is it safe to call this more than once? Or only on start?

it's called every time there's a network map update from tailcontrol

bradfitz

comment created time in 15 days

Pull request review commenttailscale/tailscale

wgengine: flesh out some docs

 type PeerStatus struct { 	NodeKey          tailcfg.NodeKey } +// Status is the Engine status. type Status struct { 	Peers      []PeerStatus 	LocalAddrs []string // TODO(crawshaw): []wgcfg.Endpoint? } -type StatusCallback func(s *Status, err error)+// StatusCallback is the type of status callbacks used by Engine.SetStatusCallback.+// TODO: exactly one of Status or error is set?+type StatusCallback func(*Status, error) +// RouteSettings is ... TODO.

It's the full WireGuard config data (set of peers keys, IP, etc is wgcfg.Config) plus things that WireGuard doesn't actually do itself: the DNS servers to use.

I don't know why LocalAddr is there, and how it differs from what's inside wgcfg.Config.

bradfitz

comment created time in 15 days

Pull request review commenttailscale/tailscale

wgengine: flesh out some docs

 func (rs *RouteSettings) OnlyRelevantParts() string { 		rs.LocalAddr, rs.DNS, rs.DNSDomains, peers) } +// Router is the TODO. type Router interface {+	// Up brings the router up.+	// TODO: more than once? after Close? 	Up() error-	SetRoutes(rs RouteSettings) error+	// SetRoutes sets the routes.+	// TODO: while running?+	SetRoutes(RouteSettings) error+	// Close closes the router.+	// TODO: return an error? does this block? 	Close() } +// Engine is the Tailscale WireGuard engine interface. type Engine interface {-	// Reconfigure wireguard and make sure it's running.+	// Reconfig reconfigures WireGuard and makes sure it's running. 	// This also handles setting up any kernel routes.+	// TODO: why is dnsDomains not in cfg itself?

because wireguard does not care about dns

bradfitz

comment created time in 15 days

Pull request review commenttailscale/tailscale

wgengine: flesh out some docs

 func NewUserspaceEngine(logf logger.Logf, tunname string, listenPort uint16, der  	tuntap, err := tun.CreateTUN(tunname, device.DefaultMTU) 	if err != nil {+		// TODO: why log.Printf here and below instead of logf?

No good reason. Should be logf (or no log at all).

bradfitz

comment created time in 15 days

Pull request review commenttailscale/tailscale

wgengine: flesh out some docs

 type PeerStatus struct { 	NodeKey          tailcfg.NodeKey } +// Status is the Engine status. type Status struct { 	Peers      []PeerStatus 	LocalAddrs []string // TODO(crawshaw): []wgcfg.Endpoint? } -type StatusCallback func(s *Status, err error)+// StatusCallback is the type of status callbacks used by Engine.SetStatusCallback.+// TODO: exactly one of Status or error is set?

It's meant to be.

bradfitz

comment created time in 15 days

pull request commenttailscale/tailscale

controlclient: handle nil Logf option

This was a quick fix of a panic in one of my test binaries. Arguably it should print to stderr with a big warning.

On Tue, Feb 11 2020 at 3:21 PM, apenwarr < notifications@github.com > wrote:

@apenwarr commented on this pull request.

Why are we allowing a nil logf? That doesn't seem like a good idea.

— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub ( https://github.com/tailscale/tailscale/pull/33?email_source=notifications&email_token=AABHMJ6446DBNUGK7OPJGL3RCMCFXA5CNFSM4KTH2GZKYY3PNVWWK3TUL52HS4DFWFIHK3DMKJSXC5LFON2FEZLWNFSXPKTDN5WW2ZLOORPWSZGOCVDOAUI#pullrequestreview-356966481 ) , or unsubscribe ( https://github.com/notifications/unsubscribe-auth/AABHMJ4POXRFAONMPTGNY2DRCMCFXANCNFSM4KTH2GZA ).

crawshaw

comment created time in 15 days

push eventtailscale/tailscale

David Crawshaw

commit sha e180bd67e6a8cf9a38849e3983c7aa2e686edeb6

controlclient: handle nil Logf option

view details

push time in 15 days

PR opened tailscale/tailscale

controlclient: handle nil Logf option
+3 -0

0 comment

1 changed file

pr created time in 15 days

push eventtailscale/tailscale

David Crawshaw

commit sha 33dfb8999e0046793b987a05d7cbec25d6c8ddba

controlclient, ipn: update tests for key pointer change Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

Tiago Reis

commit sha 1812f6afa6fe61329bd0bfb435419ec8f622228d

README.md: Fix typo on the word `versions` Signed-off-by: Tiago Reis <reis.tiago@gmail.com>

view details

Shawn Smith

commit sha 1ca83fd2059416d9023aac10fab6d539d82ad762

fix typos Signed-off-by: Shawn Smith <shawnpsmith@gmail.com>

view details

David Anderson

commit sha fb36bb9a884a0d61033f908bd14332a9aec59513

Add slack notifications to CI. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 1dfc0af361ee50dcb1e22d946e5865796fd46672

meta: create issue template configuration. The configuration directs support/product questions to info@tailscale.com, and security issues to security@tailscale.com. Signed-off-by: David Anderson <dave@natulte.net>

view details

Dave Anderson

commit sha a097f206d844c7d3da63f2687e12201ed103f5f7

Create some issue templates The goal here is to avoid support and "general product questions" from landing in Github, because we don't monitor Github for those.

view details

David Anderson

commit sha c1917710ad235af07f17419dc51d150e95df7b43

meta: revert issue templates for now. Looks like Github doesn't understand mailto: links, so we'll have to create KB pages on tailscale.com and point to those. Signed-off-by: David Anderson <dave@natulte.net>

view details

Brad Fitzpatrick

commit sha a59dc5f15533c0edfc1a27040a033f3c2c6c0a31

Use logger.Logf consistently. It was used in most places. Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>

view details

David Anderson

commit sha e59605595ea0a008e4b7e4bff79f18f409afa0b8

Add a SECURITY.md for vulnerability reports. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 39bc24cb968c93e99c2f80d39f6764bdbfd0722c

safesocket: make test use testing.T correctly. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 8380d8575ac57c9cc43a3b089c459e2d0961ed73

logtail: remove unused code from old sentinel logic. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 81eedcd5bee3ed8907e5a3a231934f44c37239df

ipnserver: ignore lint error for unused context. The linter is strictly correct, but the code is structured this way to avoid variable shadowing problems in the following for loop. The context doesn't leak. Staticcheck is correctly pointing out that this code is hard to follow. However, this chunk of code is in service of enforcing one frontend <> one backend, and we want to remove that limitation. So, we'll just ignore the lint warning until this entire piece of code goes away. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 595325c71624d5cc66c11e9ead086589eb498a52

meta: add a staticcheck CI step. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha ea11d58e96c90706293e0fe0f86c410ac0458cc7

debian: fix changelog generation. It seems changelog generation got broken by moving the code between corp and OSS repos, because one of the commit SHAs doesn't have an associated tag. In the interest of fixing the build, and because we're not yet trying to upstream the debian package, I fixed this by allowing hash-based versions to show up in the changelog. This maybe wrong from a debian standards perspective, but for our current point in life it'll work until we learn to do it better. Signed-Off-By: David Anderson <dave@natulte.net>

view details

David Crawshaw

commit sha 2b947b3b4012d6f0c35000c81159c5ff060478d1

controlclient: handle nil Logf option

view details

push time in 15 days

push eventtailscale/tailscale

David Crawshaw

commit sha 39190153de50c700809ad121579f26a3dad9dad0

controlclient: handle nil Logf option

view details

push time in 15 days

push eventtailscale/tailscale

David Crawshaw

commit sha 33dfb8999e0046793b987a05d7cbec25d6c8ddba

controlclient, ipn: update tests for key pointer change Signed-off-by: David Crawshaw <crawshaw@tailscale.com>

view details

push time in 15 days

PR opened tailscale/tailscale

controlclient, ipn: update tests for key pointer change

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

+6 -6

0 comment

3 changed files

pr created time in 15 days

push eventtailscale/tailscale

David Crawshaw

commit sha 87334fb332eeb63e7cc09709065b532105f2ae0b

wgengine, controlclient: fewer pointers in wgcfg key types Signed-off-by: David Crawshaw <david@zentus.com>

view details

David Anderson

commit sha 3dd447103660fbe0ec13f0ca34c393f06b77fe08

Add a build+test github action, as a quick and cheerful CI. Signed-Off-By: David Anderson <dave@natulte.net>

view details

wardn

commit sha aef438dfa7976a42654c704d14285d93ff13dc8c

.gitignore: ignore compiled binaries in the cmd folders Signed-off-by: wardn <wardn@users.noreply.github.com>

view details

Jimmy Zelinskie

commit sha 41d94cdcaa2f282da7d01d655a5a356ef605d2c8

init dockerfile Signed-off-by: Jimmy Zelinskie <jimmy.zelinskie+git@gmail.com>

view details

David Anderson

commit sha a07906d1d89e8c70d37355586359403ed20d5f30

wgengine: make SetRoutesFunc less appealing to use. It exists as a way to glue the mac-specific xcode logic with the OSS logic, and shouldn't be used by anything but the xcode glue.

view details

David Anderson

commit sha 452b81d56bc226f5dd62518ba641c831aa51841c

Add a code of conduct. It's verbatim from https://www.contributor-covenant.org/, except for adding in the contact info for reports. Fixes #20. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 14af0c4eb38ca193a3c4e6c95a0593349e4f6119

taillogin: check err in controlclient.New. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha fe9af19da942b191fbacdbe785c7b67cf9965dbd

controlclient: remove unnecessary comparison to bool. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 2227ede8af8a7af14cc05ec7dc1841564e065fb7

controlclient: fix staticcheck lint. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 542f46ed4dcc323931717629116ac3cb1239c66d

controlclient: remove unused function. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 1cd278aa0bf446dcd494d559ccc6dfbb28647ef1

control/policy: make error strings staticcheck-compliant. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha c3f9f74247cab4e104a0999a75ddeded9e57e689

ipn: fix lint complaints in tests. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 80ecb8342f904d3b81827ebcfa08b619eda0e375

logtail/filch: use io's seek constants. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 250dfc9016efd98e3f98d1494016b5d0e3d26f88

logtail: remove unused struct fields. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha be6bcd59cd79231a2bf18e042d392221e1dd9263

logtail: don't pass in nil contexts. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 520e96afd1caeef8c286053be1d4aaa63201916d

portlist: simplify slice expression. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 9ac1dda0d9f2df0bbc4c2523933a89c7716f05d7

portlist: add a lint ignore for unused function. The function is indeed unused on linux, but we want it around so that unit tests exercise the netstat logic even on linux, even if it's only used on !linux. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 8a339a6819a3e863dc4fd11c1e07c7ddaf2815e5

stunner: correct minor lint errors. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 43becc433490550137b142f1eb71132c3dcee162

wgengine/packet: fix minor lint errors. Signed-off-by: David Anderson <dave@natulte.net>

view details

David Anderson

commit sha 2f9cdd0aaca52d11d7402b18b139e014e6bdfd30

wgengine: fix error string. Signed-off-by: David Anderson <dave@natulte.net>

view details

push time in 15 days

issue commenttailscale/tailscale

Unused closure of code in logtail

Sentinels should be gone, not sure how this code remains. Will check it out tomorrow

danderson

comment created time in 15 days

push eventtailscale/tailscale

David Crawshaw

commit sha 87334fb332eeb63e7cc09709065b532105f2ae0b

wgengine, controlclient: fewer pointers in wgcfg key types Signed-off-by: David Crawshaw <david@zentus.com>

view details

push time in 15 days

push eventtailscale/tailscale

David Crawshaw

commit sha f3f0b7f1da359374be65997aa9fe3aba2af294b1

wgengine, controlclient: fewer pointers in wgcfg key types Signed-off-by: David Crawshaw <david@zentus.com>

view details

push time in 15 days

create barnchtailscale/tailscale

branch : crawshaw/br1

created branch time in 15 days

issue openedtailscale/tailscale

Option to fix a UDP source port number

Some firewalls only allow UDP to pass if it is from a known port. We should have a way for clients to be configured to use a predictable port.

It's not clear yet whether this is a network-wide setting, or a client setting.

created time in 16 days

issue commenttailscale/tailscale

Installing for Go noobs

The command works inside a go module. Not sure if we can make it compatible with a GOPATH.

djc

comment created time in 16 days

startedtailscale/tailscale

started time in 16 days

startedtailscale/hujson

started time in 17 days

issue commenttailscale/hujson

Name meaning

Oh dear.

Suggestions for alternate names?

pkalemba

comment created time in 17 days

more