The natlab workflows cached only the VM images, so every job re-downloaded
the tsgo toolchain and built Go from scratch. Cache the toolchain, module
cache, and build cache in both workflows. natlab-test's prepare job is the
only writer of the build cache; everything else, including its 54 matrix
jobs and natlab-basic, restores it.
The warm step compiles with both toolchains on purpose: ./tool/go builds the
test binaries, while the bare `go` that vmtest.go and gokrazy/build shell out
to is the runner's stock Go. GOCACHE keys embed the compiler's build ID, so
those entries don't interchange, but both land in ~/.cache/go-build.
Updates #13038
Signed-off-by: Brendan Creane <bcreane@gmail.com>
runProbe records IPv4CanSend or IPv6CanSend after SendPacket returns
successfully. A sufficiently fast STUN response can be received and
processed before that return, however, and the report can be cloned in
the intervening window. That produces a contradictory report with UDP
and a valid mapping, but CanSend false. Magicsock treats that as a
send failure and unnecessarily rebinds, perturbing tailcat tests.
A received STUN response itself proves that its address family sent
successfully, so also mark the family sendable while recording the
response.
Before this, the tailcat test flaked after ~3700 runs under
flakestress. Now it can run 30 minutes (12,203 successful runs).
Updates tailscale/tailcat#73
Change-Id: I54fe2e81fc703fac36f693e9dbf0c3fa27d119b1
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Previously, the new disco keys entering the client via TSMP was routed
into controlClient to allow for deduplication and filtering of disco
keys and avoid control overwriting an active TSMP learned key with a
stale key.
A system supporting multiple disco keys was introduced to allow these
keys living side by side, along side a system for selecting an active
egress key with a bias towards keys learned via TSMP. On ingress any
known key is accepted.
This PR makes the switch to key routing, by sending new TSMP learned
disco keys directly into the userspace engine and in turn magicsock,
removing the need for a full layer of deduplication in the
controlClient.
Additionally, the system that previously fully reconfigured clients on
disco key updates coming from control is now using an optimistic
handshake througha newly introduced wireguard-go method,
ScheduleHandshakeOnUserSend implemented in:
https://github.com/tailscale/wireguard-go/pull/81
What this PR does not do is revert the mapSession back to being single
writer. Bringing the mapSession back to this state is desired, however
to ensure the plumbing is done right and easier to reason about, that
will be done in a separate PR.
Updates #20590
Signed-off-by: Claus Lensbøl <claus@tailscale.com>
Add a workflow that checks out tailscale/tailcat alongside this repo and
runs its tests in a go.work workspace where tailscale.com resolves to the
tailscale.com repo at head instead of tailcat's pinned version. This gives
us an early warning when a change here breaks tailcat's API or behavior
expectations. Such a failure might be fine if the change is intentional
and coordinated with a tailcat fix, but the goal is to catch accidents.
Updates tailscale/corp#24454
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I0566437ee86bd0403c708b2f44e7ef169abf2570
go-118-fuzz-build (fc5dc53b) overlays every sibling _test.go in the fuzzer's directory onto a non-test path (<base>_libFuzzer.go). Sibling test files declaring an external test package (package foo_test) then collide with the fuzzer's package, failing the build with "found packages foo and foo_test".
One possible fix would be to stop using external _test package for our tests, but this change attempts to address this issue without changing our test packaging structure.
This change wraps each compile_native_go_fuzzer_v2 call in build_fuzzer, which hides the files in `go list .XTestGoFiles` for the build. Internal test files stay visible since the fuzz target may use their helpers.
Updates https://github.com/tailscale/corp/issues/46608
Change-Id: Ic6805854f76c597a5edff60674b5ea05b2d18ea9
Signed-off-by: Mike Jensen <mikej@tailscale.com>
MikroTik routers only support permanent UPnP leases. Previously, a
mapping attempt could proceed as follows:
1. Add a temporary mapping.
2. Retry with a permanent mapping after error 725.
3. Successfully create the permanent mapping.
4. Fail to query the external IP.
5. Forget the mapping and retry with another random port.
Each retry left another permanent NAT rule behind.
To prevent this, query and validate the external IP before creating
the mapping. When service selection has already queried the address,
reuse that result. This leaves no fallible network request after
AddPortMapping succeeds.
I tested this on my own MikroTik router running RouterOS 7.23.2. A
successful mapping queried the external IP before creating the
permanent rule, and a forced external-IP failure created no rule.
Updates #10602
RELNOTE: Prevent UPnP rule accumulation on MikroTik routers.
Change-Id: Id925ed9cc3a3da6ecb06fc892f2a934addfac37b
Signed-off-by: Jake Bailey <jacob.b.bailey@gmail.com>
The golang:N-alpine Docker image lags behind Go minor releases by a
day or two. When we bump go.mod to a new minor version before the
image catches up, the required "Build Docker image" CI check fails
with "go.mod requires go >= 1.27.1 (running go 1.27.0;
GOTOOLCHAIN=local)" and blocks the toolchain bump from merging.
Instead, base the build stage on plain alpine and download the
Tailscale Go toolchain release for the revision in go.toolchain.rev,
matching how everything else in this repo is built.
Fixes#21072
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I1ccb77fa3c7a49532ea87dbdbf9e3340880ec94e
Prior to this change there were two problems with our fuzzing for oss-fuzz:
1. There was an issue if the fuzzing spanned two files (mingled with the testing).
2. The fuzzing needs to be part of the implementation package (no _test packages).
This change fixes that by moving all package fuzzing into a common `fuzz_test.go` within the package.
Updates https://github.com/tailscale/corp/issues/46608
Change-Id: I0b95edcd0df946f723eea32f575c679214c0b202
Signed-off-by: Mike Jensen <mikej@tailscale.com>
When the wasmbuild is run from external workflows, it can't derive
the version stamps from its build context. Allow setting the
ProdLDFlags version.longStamp and version.shortStamp from the
VERSION_LONG and VERSION_SHORT environment variables when present.
This way, if the wasmbuild caller already knows them (e.g. from
mkversion), it can pass them in.
This avoids "x.y.z-ERR-BuildInfo" showing up in the admin console's
machine version column.
Updates #19707
Signed-off-by: Gesa Stupperich <gesa@tailscale.com>
When receiving disco traffic from a node, mark that node as having been
seen. Should that disco key not be the active use disco key, switch to
that one as being the active key. Additionally, clear states on the
magicsock connection and prepare for sending a new WG handshake whenever
user data is transmitted.
Sets up for:
- Routing TSMP keys directly into magicsock
- Switching the active connection reset mechanism to the optimistic
handshake
- Cleaning up paths into controlClient
Updates #20494
Signed-off-by: Claus Lensbøl <claus@tailscale.com>
Use whatever traffic steering's notion of the best connector for the
client is, rather than picking arbitrarily.
Fixestailscale/corp#46766
Signed-off-by: Fran Bull <fran@tailscale.com>
From Go 1.27.0 to Go 1.27.0 + latest upstream release branches,
plus a cherry-pick of an x/net http2 hpack memory optimization
that didn't make Go 1.27 (https://go.dev/cl/807260)
Updates #29053
Change-Id: I5c694d8c4aebf4854773ea2856177aaebb2c39a4
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
The existing TestSubnetRouterFreeBSD makes a single HTTP request, which
cannot catch a NAT rule whose translation address is wrong on average
but right occasionally: with a PF "-> (self)" rule, pf round-robins new
states across every address on the machine, so one request has decent
odds of drawing the working address while most flows hang at SYN. That
made the single-flow test an unreproducible coin flip rather than a
regression gate.
Open several fresh flows and require that all of them complete and that
the backend sees exactly one source address. PF rule counters and the
state table are dumped along the way, so a failure shows which address
each flow was translated to.
Fails 4/8 (alternating) against a "-> (self)" NAT rule, with the state
table showing the dead flows translated to the QEMU debug NIC's address
and to tailscale0's own address. Passes 8/8 with per-egress-interface
rules, and under netstack subnet routing.
Updates #5573
Change-Id: I1739ceefbab7764f1db8d07424832947b1d962a3
Signed-off-by: Martin Minkus <martin.minkus@sonic.com>
Moving FreeBSD subnet routing from netstack to the kernel changes the
behavior of every existing FreeBSD subnet router, and the kernel path is
not yet ready to be the default:
- The pf NAT rule we install never matches. On a production FreeBSD
15.0 subnet router, "pfctl -vsn -a tailscale" reports 97071
evaluations with 0 packets and 0 translations, and "pfctl -s info"
reports translate: 0. So --snat-subnet-routes=true, the default,
silently performs no source NAT at all.
- Inserting the pf anchor at runtime requires reloading the main
ruleset, which cannot preserve the contents of any table that
ruleset references. See the comment in osrouter.loadPFMainRuleset.
Netstack does its own SNAT in userspace, touches no system state, and is
what FreeBSD has always used, so keep it as the default. The kernel path
can be opted into with TS_DEBUG_NETSTACK_SUBNETS=false; only that path
can serve --snat-subnet-routes=false, which netstack cannot do because
it must rewrite the source address.
With this, TestSubnetRouterFreeBSD passes.
Updates #5573
Signed-off-by: Martin Minkus <martin.minkus@sonic.com>
removePFAnchorRef ran at shutdown (and from the startup cleanup hook) and
unconditionally rewrote the main PF ruleset to strip the "tailscale"
anchor references. That is wrong twice over for an operator who
configured those references statically in /etc/pf.conf -- the durable
setup the ensurePFAnchorRef error message recommends:
- it drifts the running ruleset away from /etc/pf.conf on every
tailscaled shutdown, and
- the rewrite reconstructs the ruleset from "pfctl -s" output, which
names PF tables but never their contents, so any table the operator
populates out-of-band is silently emptied (same failure mode
ensurePFAnchorRef now refuses).
Track whether this process inserted the references and only remove what
we added, and even then leave them alone if the ruleset now references
tables. Skipped removal is harmless: callers flush the anchor's contents
first, and a reference to an empty anchor has no effect on traffic. The
flag is process-local by design, so the cross-process startup cleanup
hook never removes references it cannot prove are tailscaled's.
The only-remove-what-we-added semantics were first identified and
implemented by Ross Williams (@overhacked) on a fork of this branch;
this is an independent implementation of the same idea alongside the
table guard.
Updates #5573
Change-Id: Idf579a8b4190c37211782feac4e5bfd057c3ace0
Signed-off-by: Martin Minkus <martin.minkus@sonic.com>
ensurePFAnchorRef reconstructs the main ruleset from "pfctl -sn" and
"pfctl -sr" output and reloads it with "pfctl -f -". That output names any
table a rule references but never prints its contents, so the reconstructed
ruleset re-declares every table as empty. Reloading it silently drops the
addresses of any table the operator populates out-of-band -- a "persist file"
table, "pfctl -T add", pfctl's own automatic tables for interface groups --
and every rule referencing that table then matches nothing.
That is a quiet, security-relevant failure: a ruleset whose "pass ... from
<trusted>" rules still exist but match no addresses looks fine in "pfctl -sr".
There is no way to insert an anchor reference into a running ruleset without
a reload, so detect the case and refuse, pointing the operator at the durable
fix (putting the anchor references in /etc/pf.conf, where they survive reboots
and pf reloads anyway).
Boxes that already have the anchor references configured are unaffected:
ensurePFAnchorRef returns early before reaching this check.
Updates #5573
Change-Id: I6efa9a63e1322af2ec8fd986a774ed76c5d5766e
Signed-off-by: Martin Minkus <martin.minkus@sonic.com>
The FreeBSD subnet-router NAT rule translated with "-> (self)":
nat on ! tailscale0 inet from 100.64.0.0/10 to any -> (self)
In pf, "(self)" is a round-robin pool of every address on the machine,
including tailscale0's own address and loopback, and pf deals each new
state the next address in the pool. Only flows that happen to draw the
egress interface's address work; a flow translated to any other address
gets replies the far end cannot route, and hangs at SYN. With N usable
addresses on the box, roughly (N-1)/N of connections through the subnet
router silently fail.
Observed in a natlab vmtest against a FreeBSD 15.0 subnet router with
four addresses (WAN, LAN, QEMU debug NIC, tailscale0): exactly half of
8 HTTP requests hung, alternating, and the pf state table showed the
failed flows translated to the debug NIC's address and to tailscale0's
own address:
10.0.0.102:51100 (100.64.0.1:35132) -> 10.0.0.103:8080 ESTABLISHED
10.0.2.15:56553 (100.64.0.1:35148) -> 10.0.0.103:8080 SYN_SENT:CLOSED
100.64.0.2:52655 (100.64.0.1:54106) -> 10.0.0.103:8080 SYN_SENT:CLOSED
On a production FreeBSD firewall running this branch, the equivalent
IPv6 rule shows 55 state creations totalling 117 packets (about two
packets per state): SYNs whose replies never came back.
Emit one rule per up, non-loopback, non-Tailscale interface instead,
translating to that interface's own address, per address family only
where the interface holds a usable address of that family:
nat on vtnet0 inet from 100.64.0.0/10 to any -> (vtnet0)
nat on vtnet1 inet from 100.64.0.0/10 to any -> (vtnet1)
which is also the rule form FreeBSD firewall operators write by hand.
With this, the same 8-request test passes 8/8, and the LAN interface's
rule shows 8 states with healthy packet counts (56 packets total).
The interface set is sampled when SNAT is enabled; interfaces added
later are not covered until SNAT is toggled or tailscaled restarts.
Updates #5573
Change-Id: Ife3367124737ce5c8785ca7f920eafca593ec705
Signed-off-by: Martin Minkus <martin.minkus@sonic.com>
CheckIPForwarding unconditionally returned a "not currently officially
supported" warning on FreeBSD without ever reading the forwarding
sysctls. Subnet routers on FreeBSD therefore got a spurious "IP
forwarding is disabled" health warning and admin console banner even
with net.inet.ip.forwarding=1 and net.inet6.ip6.forwarding=1 set.
Read the sysctls instead, and only warn for the protocols actually
required by the advertised routes. FreeBSD has no per-interface
forwarding knob, so only the global sysctls are checked.
dragonfly, netbsd and openbsd keep the previous unsupported warning.
Updates #5573
Signed-off-by: Martin Minkus <martin.minkus@sonic.com>
Use net.JoinHostPort when expanding proxy targets so IPv6 loopback addresses retain the required brackets. Accept ::1 as an HTTP and TCP destination in both current and legacy serve implementations.
Fixes#8702
Signed-off-by: James Tucker <jftucker@gmail.com>
This change adds an entry point for oss-fuzz `fuzz/oss-fuzz.sh`, allowing us to wire in our current and future fuzzing into oss-fuzz without needing to update the google/oss-fuzz repo.
Existing fuzzing was also reviewed with the following changes:
* disco/disco_fuzzer.go renamed to disco/fuzz_test.go so that it can have a _test.go suffix and match the modern go fuzzing design.
* net/stun/stun_fuzzer.go renamed to net/stun/fuzz_test.go similar to the above
* Disco and stun recieved seeds for their fuzzing starts
* All existing fuzzing was given a local round of testing, which resulted in a round trip fix for disco not handling a full zero node key.
* Running and building fuzzing was removed from CI (build only). The fuzz seeds are validated in normal go testing, but the fuzzing itself will only happen if run manually or on oss-fuzz.
Updates https://github.com/tailscale/corp/issues/46608
Change-Id: I47cb70169aefb02ac5a56220f26a6ec07fa135ee
Signed-off-by: Mike Jensen <mikej@tailscale.com>
Revision to earlier 49e148c4a3 to be robust in case somebody
unwisely edits global variable net/http.DefaultTransport.
Updates #21034
Change-Id: I0d4eb63d97c84641ffe45ac43fa4f3ccffc7446d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
The dialer returned by makeHangDialer runs on netstack-owned goroutines
that can outlive the test, so calling tb.Logf from it raced with the
test completing. Use tstest.WhileTestRunningLogger, which stops logging
once the test is done, as makeNetstack already does.
Fixes#21052
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: Ibbf4288eff48f3ae0bb5812204a1cdfd567322a3
Previously Server.HTTPClient returned a client whose transport set
only DialContext, losing http.DefaultTransport's defaults such as
ForceAttemptHTTP2, MaxIdleConns, IdleConnTimeout, TLSHandshakeTimeout,
and ExpectContinueTimeout. Now it clones http.DefaultTransport and
overrides DialContext to dial over the tailnet, so it picks up those
settings and any future defaults.
Proxy is explicitly nil: an HTTP proxy from the environment would be
dialed through the tailnet, where it's unlikely to be reachable.
The new test compares the transport field by field against a clone of
http.DefaultTransport and fails on any unknown future field, forcing a
decision about how HTTPClient should handle it.
Updates tailscale/corp#47393 (as motivation)
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I1e800982ee11d386699dd4f588c1fd2ce392f229
Dialer.Close unconditionally called PeerAPITransport, which panics
when the binary is built with the ts_omit_peerapiclient build tag,
so any such binary crashed on shutdown. Skip the idle connection
cleanup in that case; there is no peerapi transport to clean up.
Updates #12614
Change-Id: I6a8fd1860f8b74407fbb12c9a46d5fb07711e142
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
The SOCKS5 server checked the client-supplied username and password against the configured credentials with plain string equality, which returns on the first differing byte. In tsnet the password is a random 128-bit value that gates every dial out through the node, and the listener is on 127.0.0.1, so a local process can time the auth reject to recover it a byte at a time with unlimited attempts and no lockout. The LocalAPI sharing the same loopback listener already compares its credential with subtle.ConstantTimeCompare; do the same here for both fields, evaluating both so the username result does not gate whether the password is examined.
Updates #20998
Signed-off-by: basavaraj-sm05 <basavaraj@digiscrypt.com>
openresolv exits 2 when no config snippets are registered; treating that
as a failure aborted the entire DNS reconfiguration. Filtering out our own
snippet could also leave "resolvconf -l" with no arguments, which lists
every snippet including ours and would make quad-100 its own upstream.
Fixes#20825
Signed-off-by: Brendan Creane <bcreane@gmail.com>
If web proxy auto discovery is disabled system wide, net/tshttpproxy now respects this configuration and avoids connection to the proxy system service.
Updates #16813
Updates tailscale/corp#29168Fixestailscale/corp#38489
Signed-off-by: James Tucker <james@tailscale.com>
The messages needs to be received so that a node not participating in
caching can talk to a node that is participating in caching but is
unable to share its key over control.
Fixes#21008
Signed-off-by: Claus Lensbøl <claus@tailscale.com>
Promote the toolchain from Go 1.26.6 to Go 1.27.0, matching what
go.toolchain.next.rev has been testing. Besides the toolchain files
themselves (updated by pull-toolchain.sh), this bumps the go.mod go
directive, the Dockerfile golang base image, and the README, and
regenerates the depaware.txt files and the gzip assets in
tempfork/spf13/cobra and util/eventbus, whose bytes change with
Go 1.27's rewritten compress/flate.
Also bump golangci-lint to v2.13.1, the first release line built
with Go 1.27; the prebuilt v2.10.1 binary refuses to target a Go
version newer than the one it was built with.
Also bump golang.org/x/net to v0.58.0 (plus the sibling x/ module
upgrades it requires) to pick up upstream commit 8d10596d2624
(http2: avoid deadlocks in wrapped ClientConn state callback),
which we hit during Go 1.27 rc testing.
Also add docs/go-bump-checklist.md for next time.
Updates #20220
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: Ia3e4c9effafbc91227eed39efb52f1fba1b8d89c
Extend e2e test setup to work with a remote registry and real cluster.
Previously only kind was supported.
Detect cluster node architecture and build images only for the given
architecture to reduce build time. Build for all platforms as the fallback
option. This assumes only one node architecture per cluster, which is
reasonable for our test cases.
By default, --build loads the built images into a kind cluster. Use with
--registry to instead specify and push to a remote registry.
Fixestailscale/corp#46577
Signed-off-by: Becky Pauley <becky@tailscale.com>
So that when control notices a node isn't responding and sends that info
down in a netmap we will stop picking that node as a connector. This
doesn't provide great failover (it takes several minutes) but it's
better than not failing over at all.
Updates tailscale/corp#47250
Signed-off-by: Fran Bull <fran@tailscale.com>
The functions used to be used in ipn/ipnlocal but we changed that, so
the functions can be moved into feature/conn25 now.
Updates tailscale/corp#47250
Signed-off-by: Fran Bull <fran@tailscale.com>
Server.close() dereferenced s.sys.Bus unconditionally. s.sys is only
assigned partway through doInit, so it is still nil when Start fails
before that point, and Close then panics with a nil pointer dereference.
Close is reachable in that state: Start is idempotent via initOnce and
returns the stored initErr, so callers that `defer Close()` before
checking the error land in close() with s.sys == nil. Every other
resource released nearby (s.dialer, s.localAPIListener,
s.loopbackListener) is already nil-guarded; this line was the outlier.
This is reachable in practice from libtailscale/TailscaleKit, where a
failed tsnet start on darwin is followed by a close and surfaces as
EXC_BAD_ACCESS rather than the underlying start error.
Use GetOK instead of Get as well, so an unset Bus returns ok=false
rather than panicking via SubSystem.Get.
Adds TestCloseBeforeStart, which panics without this change.
Updates #20988
Signed-off-by: Prakash Rudraraju <1471544+prakashrj@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
tsnet nodes have long been able to use an exit node by setting the
ExitNodeID pref through LocalClient, but that was neither documented
nor tested.
Add a package doc section (and regenerate the README) showing how to
select an exit node via EditPrefs, and add TestDialThroughExitNode
verifying that a Dial of a non-tailnet IP is routed over WireGuard to
the exit node rather than dialed from the host network.
Fixes#20986
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: Ia1715a628245c52163083803889fd1f475abfab7
To decide whether we can bump GOAMD64 for our production binaries, we
first need to know what x86-64 microarchitecture levels the fleet's
CPUs actually support. Add two gauges to the default metric set on
linux/amd64: goamd64_capable, the maximum GOAMD64 level the host CPU
supports per /proc/cpuinfo, and goamd64_compiled, the level the
running binary was built with per its build info. Both are computed
once at init and report 0 on error.
The per-level feature lists match the checks in Go's
runtime/asm_amd64.s.
Updates tailscale/corp#47099
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I3b4e5431ae1501f0b7d584c12d9af805e9ab6125
Instead of accessing the key and string directly, put it behind a method
to make it easier to use that as a proxy when we add multiple keys
(control and TSMP origin).
Instead of only updating a single origin for disco keys, teach peermap
how to work with multiple disco key sources so a key can originate from
either control or TSMP.
This sets the work up for switching dynamically between disco keys
later. As of this commit, the keys still arrive for the most part via
the controlClient, but this commit sets us up for:
- Switching dynamically between received keys.
- Route TSMP keys directly into magicsock, not via controlClient.
- Potentially revert controlClient to the state before any TSMP
changes, making it single threaded.
- Use switching keys as trigger for optimistic WG handshakes, instead
of tearing down the full connection and setting it up again.
Updates #20494
Signed-off-by: Claus Lensbøl <claus@tailscale.com>
Invalidate the trust window so we re-run path discovery
instead of coasting on the dead path until trust lapses on its own.
Partially reverts 85bb5f8 removal of resetLocked within updateFromNode.
Fixes#20268
Change-Id: Ib18b31e4329513294b682e698d1a9f426a6a6964
Signed-off-by: Alex Valiushko <alexvaliushko@tailscale.com>
Clients can advertise an opaque app name in their ClientInfo but the
server previously did nothing with it.
Constrain app names to at most 32 bytes of printable ASCII, enforced
both in derp.NewClient and by the server when it parses the ClientInfo.
Extend the peerPresent frame, following its existing pattern of
appending optional fields, with a length-prefixed app name after the
flags byte, so trusted mesh watchers (other DERP nodes and stats
tools) can attribute connections by app. Old clients ignore the extra
bytes; old servers send frames without them.
Also add a derper --disallow-app-names flag taking a comma-separated
list of app names whose connections are refused, except for trusted
mesh peers.
Updates tailscale/corp#24454
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I6e721258675145833aafa1355fabf7fc05a5a204
Test that the client parses peerPresent frames from servers of various
eras: old servers that send fewer fields than the client knows about,
and newer servers that send trailing fields the client doesn't know
about, which it must ignore. This matters during rollouts of new DERP
servers, when a region's meshed nodes and watchers run a mix of
versions.
This is in advance of a following commit that extends the frame with a
new trailing field.
Updates tailscale/corp#24454
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I0d46126a6003a9eb07a4da46dc291872149b1a07
Clock.Advance previously fired each Ticker once per period between the
current time and the advance target, even though all but the first few
sends were dropped on the Ticker's full channel. A test that advances
the clock 180 days with a 5 second ticker registered did 3.1 million
fires, each taking a mutex, attempting a channel send, and fixing the
event heap. Under the race detector, where synchronization operations
are 20-30x more expensive, this dominated test runtime.
Now, when a tick is dropped because the channel is full, skip the
remaining missed ticks in one step, staying aligned to the original
schedule and still firing the final tick at or before the advance
target. This matches time.Ticker, which drops ticks it cannot deliver,
and is observably identical behavior. Ticks that fit in the channel are
still all delivered.
In the internal corp repo, this takes controlclient.TestExpiry under
race from 6.0s to 2.2s (its no-race time is 0.5s), and the new
benchmark improves from 449µs to 90ns per 24h advance:
BenchmarkTickerLargeAdvance-16 100 449313 ns/op (before)
BenchmarkTickerLargeAdvance-16 13311910 90.22 ns/op (after)
Updates tailscale/corp#47035
Change-Id: I4826df212415eb2ea7ceb5bc46848e816f8ccde1
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(bumping in oss mostly to get it into corp, and we require them to be in sync
for now. But we do use this in derpserver too.)
Updates tailscale/corp#46884
Change-Id: If01e3e91787e9ec4331eb9c997292239c6b379f1
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
There's a deadlock in x/net/http2 with Go 1.27 that's
fixed with v0.58. This is prep for switching to Go 1.27.
Updates #20220
Change-Id: I8e81eafc0c2f78e1760b36e9d39ac4f6046565af
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
The set-config loop uses uint16 endpoints. When Last is 65535, the increment wraps to zero and file or Unix targets continue indefinitely.
Break after applying Last so every closed range terminates without changing ordinary range behavior.
Fixes#20873
Signed-off-by: Bonobo <github@in9.at>
Pulls in https://github.com/tailscale/wireguard-go/pull/85 which
fixes an unbounded memory leak in mkIPInCIDRsTestFunc. It used a
package-level placeholder Peer, and AllowedIPs.Insert threads every
trie node onto that peer's trieEntries list, so each call leaked
its whole trie. SetAllowedIPs calls it on every netmap update, so
peer churn accumulated trie nodes until the client OOMed.
Fixestailscale/corp#47010
Signed-off-by: Adriano Sela Aviles <adriano@tailscale.com>
When a map poll's context is canceled mid-read, the error that
surfaces from the response body read depends on the transport and Go
version: Go 1.26's http2 returned the context error, while Go 1.27's
returns the underlying "use of closed network connection". Normalize
the read-error paths in sendMapRequest to report ctx.Err() when the
context was canceled so callers see a stable, meaningful error
regardless of Go version.
Updates #20220
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I5898d3f452a2e61cfdf3ee015386365943c08e70