Compare commits

...

76 Commits

Author SHA1 Message Date
salman
8e6f564f7e cmd/equaler: add command to generate Equal() methods
The generator is still crude and do not cover most types, but it
covers all the ones needed by the tailcfg package. It's a start.

Fixes #8077.

Signed-off-by: salman <salman@tailscale.com>
2023-05-06 11:36:42 +01:00
Flakes Updater
5783adcc6f go.mod.sri: update SRI hash for go.mod changes
Signed-off-by: Flakes Updater <noreply+flakes-updater@tailscale.com>
2023-05-03 19:56:23 -07:00
License Updater
503b6dd8be licenses: update tailscale{,d} licenses
Signed-off-by: License Updater <noreply+license-updater@tailscale.com>
2023-05-03 19:56:00 -07:00
Brad Fitzpatrick
9e9ea6e974 go.mod: bump all deps possible that don't break the build
This holds back gvisor, kubernetes, goreleaser, and esbuild, which all
had breaking API changes.

Updates #8043
Updates #7381
Updates #8042 (updates u-root which adds deps)

Change-Id: I889759bea057cd3963037d41f608c99eb7466a5b
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2023-05-03 19:52:54 -07:00
M. J. Fromberger
459744c9ea .github: mark bots for exemption by issuebot (#8041)
#cleanup

Change-Id: I13757ee20240166af72606d166e840ae3ee797d7
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2023-05-03 18:36:19 -07:00
License Updater
7675d323fa licenses: update tailscale{,d} licenses
Signed-off-by: License Updater <noreply@tailscale.com>
2023-05-03 15:48:09 -07:00
dependabot[bot]
270942094f build(deps): bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.16+incompatible to 20.10.24+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v20.10.16...v20.10.24)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-03 15:40:46 -07:00
Maisem Ali
be190e990f ssh/tailssh: restore support for recording locally
We removed it earlier in 916aa782af, but we still want to support it for some time longer.

Updates tailscale/corp#9967

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2023-05-03 15:00:01 -07:00
Brad Fitzpatrick
4d7927047c wgengine/magicsock: annotate, skip flaky TestIsWireGuardOnlyPickEndpointByPing
Updates #8037
Updates #7826

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2023-05-03 14:58:28 -07:00
Charlotte Brandhorst-Satzkorn
ddb4040aa0 wgengine/magicsock: add address selection for wireguard only endpoints (#7979)
This change introduces address selection for wireguard only endpoints.
If a endpoint has not been used before, an address is randomly selected
to be used based on information we know about, such as if they are able
to use IPv4 or IPv6. When an address is initially selected, we also
initiate a new ICMP ping to the endpoints addresses to determine which
endpoint offers the best latency. This information is then used to
update which endpoint we should be using based on the best possible
route. If the latency is the same for a IPv4 and an IPv6 address, IPv6
will be used.

Updates #7826

Signed-off-by: Charlotte Brandhorst-Satzkorn <charlotte@tailscale.com>
2023-05-02 17:49:56 -07:00
Brad Fitzpatrick
c1e6888fc7 derp: add a README.md with some docs
Updates #docs

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2023-05-02 13:42:25 -07:00
Maisem Ali
3ae7140690 net/tstun: handle exit nodes in NAT configs
In the case where the exit node requires SNAT, we would SNAT all traffic not just the
traffic meant to go through the exit node. This was a result of the default route being
added to the routing table which would match basically everything.

In this case, we need to account for all peers in the routing table not just the ones
that require NAT.

Fix and add a test.

Updates tailscale/corp#8020

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2023-05-02 13:03:30 -07:00
Andrew Dunham
bcf7b63d7e wgengine/magicsock: add hysteresis to endpoint selection
Avoid selecting an endpoint as "better" than the current endpoint if the
total latency improvement is less than 1%. This adds some hysteresis to
avoid flapping between endpoints for a minimal improvement in latency.

Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
Change-Id: If8312e1768ea65c4b4d4e13d8de284b3825d7a73
2023-05-02 08:56:16 -07:00
Tom DNetto
c5bf868940 ssh/tailssh: improve debug logging around revoked sessions
Updates https://github.com/tailscale/corp/issues/10943
Signed-off-by: Tom DNetto <tom@tailscale.com>
2023-05-01 14:10:16 -07:00
Andrew Dunham
42fd964090 control/controlclient: use dnscache.Resolver for Noise client
This passes the *dnscache.Resolver down from the Direct client into the
Noise client and from there into the controlhttp client. This retains
the Resolver so that it can share state across calls instead of creating
a new resolver.

Updates #4845
Updates #6110

Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
Change-Id: Ia5d6af1870f3b5b5d7dd5685d775dcf300aec7af
2023-05-01 13:22:10 -07:00
License Updater
979d29b5f5 licenses: update win/apple licenses
Signed-off-by: License Updater <noreply+license-updater@tailscale.com>
2023-05-01 09:13:43 -07:00
Anton Tolchanov
1f4a34588b .github: test installer script in CI in docker
Every time we change `installer.sh`, run it in a few docker
containers based on different Linux distros, just as a simple test.

Also includes a few changes to the installer script itself to make
installation work in docker:
- install dnf config-manager command before running it
- run zypper in non-interactive mode
- update pacman indexes before installing packages

Updates https://github.com/tailscale/corp/issues/8952

Signed-off-by: Anton Tolchanov <anton@tailscale.com>
2023-04-29 08:48:19 +01:00
Denton Gentry
a82f275619 cmd/sniproxy: Set App name in tsnet hostinfo
Updates #1748
Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2023-04-28 21:50:30 -07:00
James Tucker
b3c3a9f174 syncs: add Map.Len to get the length of the Map
I need this for a corp change where I have a set as a queue, and make a
different decisison if the set is empty.

Updates tailscale/corp#10344

Signed-off-by: James Tucker <james@tailscale.com>
2023-04-27 19:10:28 -07:00
ayanamist
042f82ea32 build_dist.sh: make cross-compilation friendly for env CC specified
Signed-off-by: ayanamist <ayanamist@gmail.com>
2023-04-27 18:51:59 -07:00
dependabot[bot]
633d08bd7b .github: Bump actions/setup-go from 3 to 4
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-27 17:21:59 -07:00
James Tucker
d35ce1add9 syncs: add documentation to Map.Range
Updates #cleanup

Signed-off-by: James Tucker <james@tailscale.com>
2023-04-27 17:07:07 -07:00
Charlotte Brandhorst-Satzkorn
c3ab36cb9d words: charlotte 1 spell-checking-linter 0 (#7993)
Can't have a dupe when the dupe is wrong. Clearly we need to up
our spell checking game. Did anyone say AI?

Signed-off-by: Charlotte Brandhorst-Satzkorn <charlotte@tailscale.com>
2023-04-27 08:58:52 -07:00
James Tucker
8032b966a1 .github/workflows: add recency bias to action cache keys
The action cache restore process either matches the restore key pattern
exactly, or uses a matching prefix with the most recent date.

If the restore key is an exact match, then no updates are uploaded, but
if we've just computed tests executions for more recent code then we
will likely want to use those results in future runs.

Appending run_id to the cache key will give us an always new key, and
then we will be restore a recently uploaded cache that is more likely
has a higher overlap with the code being tested.

Updates #7975

Signed-off-by: James Tucker <james@tailscale.com>
2023-04-26 21:36:53 -07:00
Kyle Carberry
d78b334964 cmd/derper: disable http2
DERP doesn't support HTTP/2. If an HTTP/2 proxy was placed in front of
a DERP server requests would fail because the connection would
be initialized with HTTP/2, which the DERP client doesn't support.

Signed-off-by: Kyle Carberry <kyle@carberry.com>
2023-04-26 20:45:32 -07:00
Charlotte Brandhorst-Satzkorn
161d1d281a net/ping,netcheck: add v6 pinging capabilities to pinger (#7971)
This change adds a v6conn to the pinger to enable sending pings to v6
addrs.

Updates #7826

Signed-off-by: Charlotte Brandhorst-Satzkorn <charlotte@tailscale.com>
2023-04-26 15:59:37 -07:00
Denton Gentry
1145b9751d VERSION.txt: this is v1.41.0
Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2023-04-26 15:58:30 -07:00
Brad Fitzpatrick
1e876a3c1d ipn/ipnlocal: fix fmt format arg type mismatch in log line
It was printing like "v0xxxxxxxx" after version.Long became a func
in 8b2ae47c31.

Fixes #7976

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2023-04-26 14:28:09 -07:00
Maisem Ali
a8f10c23b2 cmd/tailscale/cli: [up] reuse --advertise-tags for OAuth key generation
We need to always specify tags when creating an AuthKey from an OAuth key.

Check for that, and reuse the `--advertise-tags` param.

Updates #7982

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2023-04-26 14:17:29 -07:00
Brad Fitzpatrick
b2b5379348 cmd/tailscale/cli: [up] change oauth authkey format
Updates #7982

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2023-04-26 13:36:17 -07:00
Brad Fitzpatrick
13de36303d cmd/tailscale/cli: [up] add experimental oauth2 authkey support
Updates #7982

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2023-04-26 12:25:42 -07:00
James Tucker
095d3edd33 ipn/ipnlocal: reenable profile tests on Windows
This fix does not seem ideal, but the test infrastructure using a local
goos doesn't seem to avoid all of the associated challenges, but is
somewhat deeply tied to the setup.

The core issue this addresses for now is that when run on Windows there
can be no code paths that attempt to use an invalid UID string, which on
Windows is described in [1].

For the goos="linux" tests, we now explicitly skip the affected
migration code if runtime.GOOS=="windows", and for the Windows test we
explicitly use the running users uid, rather than just the string
"user1". We also now make the case where a profile exists and has
already been migrated a non-error condition toward the outer API.

Updates #7876

[1] https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers

Signed-off-by: James Tucker <jftucker@gmail.com>
2023-04-26 11:33:38 -07:00
James Tucker
43819309e1 .github/workflows: split tests and benchmarks for caching
Benchmark flags prevent test caching, so benchmarks are now executed
independently of tests.

Fixes #7975

Signed-off-by: James Tucker <james@tailscale.com>
2023-04-26 10:49:38 -07:00
Maisem Ali
1b8a0dfe5e ssh/tailssh: also handle recording upload failure during writes
Previously we would error out when the recording server disappeared after the in memory
buffer filled up for the io.Copy. This makes it so that we handle failing open correctly
in that path.

Updates tailscale/corp#9967

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2023-04-25 19:40:46 -07:00
shayne
018a382729 cmd/tailscale/cli: [serve] fix MinGW path conversion (#7964)
Fixes #7963

Signed-off-by: Shayne Sweeney <shayne@tailscale.com>
2023-04-25 13:07:17 -04:00
License Updater
2e07245384 licenses: update android licenses
Signed-off-by: License Updater <noreply@tailscale.com>
2023-04-25 09:10:26 -07:00
License Updater
aa87e999dc licenses: update win/apple licenses
Signed-off-by: License Updater <noreply+license-updater@tailscale.com>
2023-04-25 09:10:05 -07:00
Maisem Ali
f58751eb2b net/packet: add NAT support for DCCP and GRE
Updates tailscale/corp#8020

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2023-04-24 15:30:50 -07:00
Marwan Sulaiman
ce11c82d51 ipn/store/awsstore: persist state with intelligent tiering
Fixes #6784

This PR makes it so that we can persist the tailscaled state with
intelligent tiering which increases the capacity from 4kb to 8kb

Signed-off-by: Marwan Sulaiman <marwan@tailscale.com>
2023-04-24 14:35:13 -04:00
Brad Fitzpatrick
90ba26cea1 net/netcheck: fix crash when IPv6 kinda but not really works
Looks like on some systems there's an IPv6 address, but then opening
a IPv6 UDP socket fails later. Probably some firewall. Tolerate it
better and don't crash.

To repro: check the "udp6" to something like "udp7" (something that'll
fail) and run "go run ./cmd/tailscale netcheck" on a machine with
active IPv6. It used to crash and now it doesn't.

Fixes #7949

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2023-04-22 17:53:39 -07:00
Maisem Ali
7778d708a6 ssh/tailssh: handle dialing multiple recorders and failing open
This adds support to try dialing out to multiple recorders each
with a 5s timeout and an overall 30s timeout. It also starts respecting
the actions `OnRecordingFailure` field if set, if it is not set
it fails open.

Updates tailscale/corp#9967

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2023-04-22 10:23:13 -07:00
Maisem Ali
f66ddb544c tailcfg: add SSHRecorderFailureAction and SSHRecordingFailureNotifyRequest
This allows control to specify how to handle situations where the recorder
isn't available.

Updates tailscale/corp#9967

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2023-04-22 10:23:13 -07:00
Flakes Updater
e3b2250e26 go.mod.sri: update SRI hash for go.mod changes
Signed-off-by: Flakes Updater <noreply@tailscale.com>
2023-04-21 21:36:21 -07:00
Andrew Dunham
6f521c138d tailcfg: add CanPort80 field to DERPNode
A follow-up PR will start using this field after we set it in our
production DERPMap.

Updates #7925

Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
Change-Id: Idb41b79e6055dddb8944f79d91ad4a186ace98c7
2023-04-21 14:15:26 -04:00
Andrew Dunham
04a3118d45 net/tstun: add tests for captureHook
Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
Change-Id: I630f852d9f16c951c721b34f2bc4128e68fe9475
2023-04-21 14:05:20 -04:00
Denton Gentry
c791e64881 scripts/installer: add Deepin, RisiOS.
Fixes https://github.com/tailscale/tailscale/issues/7862
Fixes https://github.com/tailscale/tailscale/issues/7899

Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2023-04-20 18:51:50 -07:00
Mihai Parparita
7330aa593e all: avoid repeated default interface lookups
On some platforms (notably macOS and iOS) we look up the default
interface to bind outgoing connections to. This is both duplicated
work and results in logspam when the default interface is not available
(i.e. when a phone has no connectivity, we log an error and thus cause
more things that we will try to upload and fail).

Fixed by passing around a netmon.Monitor to more places, so that we can
use its cached interface state.

Fixes #7850
Updates #7621

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2023-04-20 15:46:01 -07:00
Will Norris
7f17e04a5a log/sockstatlog: bump logInterval to 10 seconds
We are seeing indications that some devices are still getting into an
upload loop.  Bump logInterval in case these devices are on slow
connections that are taking more than 3 seconds to uploads sockstats.

Updates #7719

Signed-off-by: Will Norris <will@tailscale.com>
2023-04-20 11:37:06 -07:00
Mihai Parparita
4722f7e322 all: move network monitoring from wgengine/monitor to net/netmon
We're using it in more and more places, and it's not really specific to
our use of Wireguard (and does more just link/interface monitoring).

Also removes the separate interface we had for it in sockstats -- it's
a small enough package (we already pull in all of its dependencies
via other paths) that it's not worth the extra complexity.

Updates #7621
Updates #7850

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2023-04-20 10:15:59 -07:00
Andrew Dunham
3ede3aafe4 ipn/localapi: also verify STUN queries work in 'debug derp'
Updates #6526

Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
Change-Id: I274b7ed53ee0be3fb94fdb00cafe06a1d676e1cf
2023-04-20 10:14:59 -04:00
James Tucker
f844791e15 safesocket: enable test to run on Windows unpriviliged
I manually tested that the code path that relaxes pipe permissions is
not executed when run with elevated priviliges, and the test also passes
in that case.

Updates #7876

Signed-off-by: James Tucker <jftucker@gmail.com>
2023-04-19 19:06:18 -07:00
James Tucker
cd35a79136 syncs: relax TestWatchMultipleValues timing on Windows
The test is re-enabled for Windows with a relaxed time assertion.

On Windows the runtime poller currently does not have sufficient
resolution to meet the normal requirements for this test.

See https://github.com/golang/go/issues/44343 for background.

Updates #7876

Signed-off-by: James Tucker <jftucker@gmail.com>
2023-04-19 19:00:34 -07:00
Andrew Dunham
f85dc6f97c ci: add more lints (#7909)
This is a follow-up to #7905 that adds two more linters and fixes the corresponding findings. As per the previous PR, this only flags things that are "obviously" wrong, and fixes the issues found.

Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
Change-Id: I8739bdb7bc4f75666a7385a7a26d56ec13741b7c
2023-04-19 21:54:19 -04:00
dependabot[bot]
5acc7c4b1e .github: Bump ruby/action-slack from 3.0.0 to 3.2.1
Bumps [ruby/action-slack](https://github.com/ruby/action-slack) from 3.0.0 to 3.2.1.
- [Release notes](https://github.com/ruby/action-slack/releases)
- [Commits](https://github.com/ruby/action-slack/compare/v3.0.0...v3.2.1)

---
updated-dependencies:
- dependency-name: ruby/action-slack
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-19 18:41:00 -07:00
dependabot[bot]
c328770184 .github: Bump peter-evans/create-pull-request from 4.2.4 to 5.0.0
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.2.4 to 5.0.0.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](38e0b6e68b...5b4a9f6a9e)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-19 18:38:14 -07:00
License Updater
588a234fdc licenses: update win/apple licenses
Signed-off-by: License Updater <noreply+license-updater@tailscale.com>
2023-04-19 17:06:12 -07:00
Maisem Ali
c3ef6fb4ee ipn/ipnlocal: handle masquerade addresses in PeerAPI
Without this, the peer fails to do anything over the PeerAPI if it
has a masquerade address.

```
Apr 19 13:58:15 hydrogen tailscaled[6696]: peerapi: invalid request from <ip>:58334: 100.64.0.1/32 not found in self addresses
```

Updates #8020

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2023-04-19 15:51:44 -07:00
Maisem Ali
85de580455 net/tsdial: do not use proxies when dialing out to PeerAPI
Found this when adding a test that does a ping over PeerAPI.

Our integration tests set up a trafficTrap to ensure that tailscaled
does not call out to the internet, and it does so via a HTTP_PROXY.

When adding a test for pings over PeerAPI, it triggered the trap and investigation
lead to the realization that we were not removing the Proxy when trying to
dial out to the PeerAPI.

Updates tailscale/corp#8020

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2023-04-19 15:51:44 -07:00
Mihai Parparita
d0906cda97 net/sockstats: expose debug info
Exposes some internal state of the sockstats package via the C2N and
PeerAPI endpoints, so that it can be used for debugging. For now this
includes the estimated radio on percentage and a second-by-second view
of the times the radio was active.

Also fixes another off-by-one error in the radio on percentage that
was leading to >100% values (if n seconds have passed since we started
to monitor, there may be n + 1 possible seconds where the radio could
have been on).

Updates tailscale/corp#9230

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2023-04-19 14:33:12 -07:00
Will Norris
7c386ca6d2 net/sockstats: fix calculation of radio power usage
When splitting the radio monitor usage array, we were splitting at now %
3600 to get values into chronological order.  This caused the value for
the final second to be included at the beginning of the ordered slice
rather than the end.  If there was activity during that final second, an
extra five seconds of high power usage would get recorded in some cases.
This could result in a final calculation of greater than 100% usage.

This corrects that by splitting values at (now+1 % 3600).

This also simplifies the percentage calculation by always rounding
values down, which is sufficient for our usage.

Signed-off-by: Will Norris <will@tailscale.com>
2023-04-19 13:18:02 -07:00
License Updater
7f057d7489 licenses: update tailscale{,d} licenses
Signed-off-by: License Updater <noreply@tailscale.com>
2023-04-19 10:22:36 -07:00
Mihai Parparita
c7cea825ae net/netns: don't log errors when we can't get the default route on Darwin
It's somewhat common (e.g. when a phone has no reception), and leads to
lots of logspam.

Updates #7850

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2023-04-18 09:57:15 -07:00
Andrew Dunham
280255acae various: add golangci-lint, fix issues (#7905)
This adds an initial and intentionally minimal configuration for
golang-ci, fixes the issues reported, and adds a GitHub Action to check
new pull requests against this linter configuration.

Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
Change-Id: I8f38fbc315836a19a094d0d3e986758b9313f163
2023-04-17 18:38:24 -04:00
Joe Tsai
ff1b35ec6c net/connstats: exclude traffic with internal Tailscale service (#7904)
Exclude traffic with 100.100.100.100 (for IPv4) and
with fd7a:115c:a1e0::53 (for IPv6) since this traffic with the
Tailscale service running locally on the node.
This traffic never left the node.

It also happens to be a high volume amount of traffic since
DNS requests occur over UDP with each request coming from a
unique port, thus resulting in many discrete traffic flows.

Fixes tailscale/corp#10554

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2023-04-17 14:24:29 -07:00
Mihai Parparita
9a655a1d58 net/dnsfallback: more explicitly pass through logf function
Redoes the approach from #5550 and #7539 to explicitly pass in the logf
function, instead of having global state that can be overridden.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2023-04-17 12:06:23 -07:00
dependabot[bot]
28cb1221ba .github: Bump actions/setup-go from 3 to 4
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-17 11:51:30 -07:00
Brad Fitzpatrick
d5a870b4dc wgengine/monitor: add --monitor-duration flag to opt-in TestMonitorMode
TestMonitorMode skips by default, without the --monitor flag, and then
it previously ran forever. This adds an option --monitor-duration flag
that defaults to zero (run forever) but if non-zero bounds how long
the tests runs. This means you can then also use e.g. `go test
--cpuprofile` and capture a CPU/mem profile for a minute or two.

Updates #7621

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2023-04-17 11:14:40 -07:00
Brad Fitzpatrick
162488a775 net/interfaces: cache "home" router lookup on big Linux routers
This is a continuation of the earlier 2a67beaacf but more aggressive;
this now remembers that we failed to find the "home" router IP so we
don't try again later on the next call.

Updates #7621

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2023-04-17 10:51:21 -07:00
James Tucker
c5150eae67 net/netcheck: reenable TestNodeAddrResolve on Windows
Updates #7876

Co-authored-by: Andrew Dunham <andrew@du.nham.ca>
Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
Signed-off-by: James Tucker <james@tailscale.com>
Change-Id: Idb2e6cc2edf6ca123b751d6c8f8729b0cba86023
2023-04-17 12:41:56 -04:00
Andrew Dunham
80b138f0df wgengine/magicsock: keep advertising endpoints after we stop discovering them
Previously, when updating endpoints we would immediately stop
advertising any endpoint that wasn't discovered during
determineEndpoints. This could result in, for example, a case where we
performed an incremental netcheck, didn't get any of our three STUN
packets back, and then dropped our STUN endpoint from the set of
advertised endpoints... which would result in clients falling back to a
DERP connection until the next call to determineEndpoints.

Instead, let's cache endpoints that we've discovered and continue
reporting them to clients until a timeout expires. In the above case
where we temporarily don't have a discovered STUN endpoint, we would
continue reporting the old value, then re-discover the STUN endpoint
again and continue reporting it as normal, so clients never see a
withdrawal.

Updates tailscale/coral#108

Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
Change-Id: I42de72e7418ab328a6c732bdefc74549708cf8b9
2023-04-17 11:26:02 -04:00
Brad Fitzpatrick
4b49ca4a12 wgengine/magicsock: update comments on what implements conn.Bind
The comment still said *magicsock.Conn implemented wireguard-go conn.Bind.
That wasn't accurate anymore.

A doc #cleanup.

Change-Id: I7fd003b939497889cc81147bfb937b93e4f6865c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2023-04-16 09:07:13 -07:00
Brad Fitzpatrick
10f1c90f4d wgengine/magicsock, types/nettype, etc: finish ReadFromUDPAddrPort netip migration
So we're staying within the netip.Addr/AddrPort consistently and
avoiding allocs/conversions to the legacy net addr types.

Updates #5162

Change-Id: I59feba60d3de39f773e68292d759766bac98c917
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2023-04-15 13:40:15 -07:00
Brad Fitzpatrick
29f7df9d8f wgengine/magicsock, etc: remove mostly unused WriteTo methods
Updates #2331
Updates #5162

Change-Id: I8291884425481eeaedde38a54adfd8ed7292a497
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2023-04-15 08:32:11 -07:00
Brad Fitzpatrick
83c41f3697 net/netcheck: remove unused method from interface
Updates #2331
Updates #5162

Change-Id: I77ff956c2d59bde611d47523659a07afb4a6da2d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2023-04-15 07:55:43 -07:00
James Tucker
20f17d6e7b wgengine/magicsock: reenable magicsock tests on Windows
These tests are passing locally and on CI. They had failed earlier in
the day when first fixing up CI, and it is not immediately clear why. I
have cycled IPv6 support locally, but this should not have a substantial
effect.

Updates #7876

Signed-off-by: James Tucker <jftucker@gmail.com>
2023-04-14 22:53:53 -07:00
James Tucker
bd0c32ca21 tsnet: disable TestLoopbackSOCKS5 on Windows
This test is not regularly passing on CI, but seems to pass reliably
locally. Needs deeper debugging.

Updates #7876

Signed-off-by: James Tucker <jftucker@gmail.com>
2023-04-14 22:28:04 -07:00
172 changed files with 4740 additions and 2164 deletions

View File

@@ -50,11 +50,11 @@ jobs:
private_key: ${{ secrets.LICENSING_APP_PRIVATE_KEY }}
- name: Send pull request
uses: peter-evans/create-pull-request@38e0b6e68b4c852a5500a94740f0e535e0d7ba54 #v4.2.4
uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 #v5.0.0
with:
token: ${{ steps.generate-token.outputs.token }}
author: License Updater <noreply@tailscale.com>
committer: License Updater <noreply@tailscale.com>
author: License Updater <noreply+license-updater@tailscale.com>
committer: License Updater <noreply+license-updater@tailscale.com>
branch: licenses/cli
commit-message: "licenses: update tailscale{,d} licenses"
title: "licenses: update tailscale{,d} licenses"

40
.github/workflows/golangci-lint.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: golangci-lint
on:
# For now, only lint pull requests, not the main branches.
pull_request:
# TODO(andrew): enable for main branch after an initial waiting period.
#push:
# branches:
# - main
workflow_dispatch:
permissions:
contents: read
pull-requests: read
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version-file: go.mod
cache: false
- name: golangci-lint
# Note: this is the 'v3' tag as of 2023-04-17
uses: golangci/golangci-lint-action@08e2f20817b15149a52b5b3ebe7de50aff2ba8c5
with:
version: v1.52.2
# Show only new issues if it's a pull request.
only-new-issues: true

102
.github/workflows/installer.yml vendored Normal file
View File

@@ -0,0 +1,102 @@
name: test installer.sh
on:
push:
branches:
- "main"
paths:
- scripts/installer.sh
pull_request:
branches:
- "*"
paths:
- scripts/installer.sh
jobs:
test:
strategy:
# Don't abort the entire matrix if one element fails.
fail-fast: false
# Don't start all of these at once, which could saturate Github workers.
max-parallel: 4
matrix:
image:
# This is a list of Docker images against which we test our installer.
# If you find that some of these no longer exist, please feel free
# to remove them from the list.
# When adding new images, please only use official ones.
- "debian:oldstable-slim"
- "debian:stable-slim"
- "debian:testing-slim"
- "debian:sid-slim"
- "ubuntu:18.04"
- "ubuntu:20.04"
- "ubuntu:22.04"
- "ubuntu:22.10"
- "ubuntu:23.04"
- "elementary/docker:stable"
- "elementary/docker:unstable"
- "parrotsec/core:lts-amd64"
- "parrotsec/core:latest"
- "kalilinux/kali-rolling"
- "kalilinux/kali-dev"
- "oraclelinux:9"
- "oraclelinux:8"
- "fedora:latest"
- "rockylinux:8.7"
- "rockylinux:9"
- "amazonlinux:latest"
- "opensuse/leap:latest"
- "opensuse/tumbleweed:latest"
- "archlinux:latest"
- "alpine:3.14"
- "alpine:latest"
- "alpine:edge"
deps:
# Run all images installing curl as a dependency.
- curl
include:
# Check a few images with wget rather than curl.
- { image: "debian:oldstable-slim", deps: "wget" }
- { image: "debian:sid-slim", deps: "wget" }
- { image: "ubuntu:23.04", deps: "wget" }
# Ubuntu 16.04 also needs apt-transport-https installed.
- { image: "ubuntu:16.04", deps: "curl apt-transport-https" }
- { image: "ubuntu:16.04", deps: "wget apt-transport-https" }
runs-on: ubuntu-latest
container:
image: ${{ matrix.image }}
options: --user root
steps:
- name: install dependencies (yum)
# tar and gzip are needed by the actions/checkout below.
run: yum install -y --allowerasing tar gzip ${{ matrix.deps }}
if: |
contains(matrix.image, 'centos')
|| contains(matrix.image, 'oraclelinux')
|| contains(matrix.image, 'fedora')
|| contains(matrix.image, 'amazonlinux')
- name: install dependencies (zypper)
# tar and gzip are needed by the actions/checkout below.
run: zypper --non-interactive install tar gzip
if: contains(matrix.image, 'opensuse')
- name: install dependencies (apt-get)
run: |
apt-get update
apt-get install -y ${{ matrix.deps }}
if: |
contains(matrix.image, 'debian')
|| contains(matrix.image, 'ubuntu')
|| contains(matrix.image, 'elementary')
|| contains(matrix.image, 'parrotsec')
|| contains(matrix.image, 'kalilinux')
- name: checkout
uses: actions/checkout@v3
- name: run installer
run: scripts/installer.sh
# Package installation can fail in docker because systemd is not running
# as PID 1, so ignore errors at this step. The real check is the
# `tailscale --version` command below.
continue-on-error: true
- name: check tailscale version
run: tailscale --version

View File

@@ -65,8 +65,9 @@ jobs:
~\AppData\Local\go-build
# The -2- here should be incremented when the scheme of data to be
# cached changes (e.g. path above changes).
key: ${{ github.job }}-${{ runner.os }}-${{ matrix.goarch }}-${{ matrix.buildflags }}-go-2-${{ hashFiles('**/go.sum') }}
key: ${{ github.job }}-${{ runner.os }}-${{ matrix.goarch }}-${{ matrix.buildflags }}-go-2-${{ hashFiles('**/go.sum') }}-${{ github.run_id }}
restore-keys: |
${{ github.job }}-${{ runner.os }}-${{ matrix.goarch }}-${{ matrix.buildflags }}-go-2-${{ hashFiles('**/go.sum') }}
${{ github.job }}-${{ runner.os }}-${{ matrix.goarch }}-${{ matrix.buildflags }}-go-2-
- name: build all
run: ./tool/go build ${{matrix.buildflags}} ./...
@@ -89,7 +90,11 @@ jobs:
- name: build test wrapper
run: ./tool/go build -o /tmp/testwrapper ./cmd/testwrapper
- name: test all
run: ./tool/go test ${{matrix.buildflags}} -exec=/tmp/testwrapper -bench=. -benchtime=1x ./...
run: ./tool/go test ${{matrix.buildflags}} -exec=/tmp/testwrapper
env:
GOARCH: ${{ matrix.goarch }}
- name: bench all
run: ./tool/go test ${{matrix.buildflags}} -exec=/tmp/testwrapper -test.bench=. -test.benchtime=1x -test.run=^$
env:
GOARCH: ${{ matrix.goarch }}
- name: check that no tracked files changed
@@ -113,7 +118,7 @@ jobs:
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v4
with:
go-version-file: go.mod
cache: false
@@ -131,8 +136,9 @@ jobs:
~\AppData\Local\go-build
# The -2- here should be incremented when the scheme of data to be
# cached changes (e.g. path above changes).
key: ${{ github.job }}-${{ runner.os }}-go-2-${{ hashFiles('**/go.sum') }}
key: ${{ github.job }}-${{ runner.os }}-go-2-${{ hashFiles('**/go.sum') }}-${{ github.run_id }}
restore-keys: |
${{ github.job }}-${{ runner.os }}-go-2-${{ hashFiles('**/go.sum') }}
${{ github.job }}-${{ runner.os }}-go-2-
- name: test
# Don't use -bench=. -benchtime=1x.
@@ -206,8 +212,9 @@ jobs:
~\AppData\Local\go-build
# The -2- here should be incremented when the scheme of data to be
# cached changes (e.g. path above changes).
key: ${{ github.job }}-${{ runner.os }}-${{ matrix.goos }}-${{ matrix.goarch }}-go-2-${{ hashFiles('**/go.sum') }}
key: ${{ github.job }}-${{ runner.os }}-${{ matrix.goos }}-${{ matrix.goarch }}-go-2-${{ hashFiles('**/go.sum') }}-${{ github.run_id }}
restore-keys: |
${{ github.job }}-${{ runner.os }}-${{ matrix.goos }}-${{ matrix.goarch }}-go-2-${{ hashFiles('**/go.sum') }}
${{ github.job }}-${{ runner.os }}-${{ matrix.goos }}-${{ matrix.goarch }}-go-2-
- name: build all
run: ./tool/go build ./cmd/...
@@ -271,8 +278,9 @@ jobs:
~\AppData\Local\go-build
# The -2- here should be incremented when the scheme of data to be
# cached changes (e.g. path above changes).
key: ${{ github.job }}-${{ runner.os }}-go-2-${{ hashFiles('**/go.sum') }}
key: ${{ github.job }}-${{ runner.os }}-go-2-${{ hashFiles('**/go.sum') }}-${{ github.run_id }}
restore-keys: |
${{ github.job }}-${{ runner.os }}-go-2-${{ hashFiles('**/go.sum') }}
${{ github.job }}-${{ runner.os }}-go-2-
- name: build tsconnect client
run: ./tool/go build ./cmd/tsconnect/wasm ./cmd/tailscale/cli
@@ -450,7 +458,7 @@ jobs:
# By having the job always run, but skipping its only step as needed, we
# let the CI output collapse nicely in PRs.
if: failure() && github.event_name == 'push'
uses: ruby/action-slack@v3.0.0
uses: ruby/action-slack@v3.2.1
with:
payload: |
{

View File

@@ -35,11 +35,11 @@ jobs:
private_key: ${{ secrets.LICENSING_APP_PRIVATE_KEY }}
- name: Send pull request
uses: peter-evans/create-pull-request@38e0b6e68b4c852a5500a94740f0e535e0d7ba54 #v4.2.4
uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 #v5.0.0
with:
token: ${{ steps.generate-token.outputs.token }}
author: Flakes Updater <noreply@tailscale.com>
committer: Flakes Updater <noreply@tailscale.com>
author: Flakes Updater <noreply+flakes-updater@tailscale.com>
committer: Flakes Updater <noreply+flakes-updater@tailscale.com>
branch: flakes
commit-message: "go.mod.sri: update SRI hash for go.mod changes"
title: "go.mod.sri: update SRI hash for go.mod changes"

61
.golangci.yml Normal file
View File

@@ -0,0 +1,61 @@
linters:
# Don't enable any linters by default; just the ones that we explicitly
# enable in the list below.
disable-all: true
enable:
- bidichk
- gofmt
- goimports
- misspell
- revive
# Configuration for how we run golangci-lint
run:
timeout: 5m
issues:
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
# These are forks of an upstream package and thus are exempt from stylistic
# changes that would make pulling in upstream changes harder.
- path: tempfork/.*\.go
text: "File is not `gofmt`-ed with `-s` `-r 'interface{} -> any'`"
- path: util/singleflight/.*\.go
text: "File is not `gofmt`-ed with `-s` `-r 'interface{} -> any'`"
# Per-linter settings are contained in this top-level key
linters-settings:
# Enable all rules by default; we don't use invisible unicode runes.
bidichk:
gofmt:
rewrite-rules:
- pattern: 'interface{}'
replacement: 'any'
goimports:
misspell:
revive:
enable-all-rules: false
ignore-generated-header: true
rules:
- name: atomic
- name: context-keys-type
- name: defer
arguments: [[
# Calling 'recover' at the time a defer is registered (i.e. "defer recover()") has no effect.
"immediate-recover",
# Calling 'recover' outside of a deferred function has no effect
"recover",
# Returning values from a deferred function has no effect
"return",
]]
- name: duplicated-imports
- name: errorf
- name: string-of-int
- name: time-equal
- name: unconditional-recursion
- name: useless-break
- name: waitgroup-by-value

View File

@@ -1 +1 @@
1.39.0
1.41.0

View File

@@ -30,7 +30,7 @@ func TestDoesNotOverwriteIrregularFiles(t *testing.T) {
}
// The least troublesome thing to make that is not a file is a unix socket.
// Making a null device sadly requries root.
// Making a null device sadly requires root.
l, err := net.ListenUnix("unix", &net.UnixAddr{Name: path, Net: "unix"})
if err != nil {
t.Fatal(err)

View File

@@ -16,7 +16,7 @@ if [ -n "${TS_USE_TOOLCHAIN:-}" ]; then
go="./tool/go"
fi
eval `GOOS=$($go env GOHOSTOS) GOARCH=$($go env GOHOSTARCH) $go run ./cmd/mkversion`
eval `CGO_ENABLED=0 GOOS=$($go env GOHOSTOS) GOARCH=$($go env GOHOSTARCH) $go run ./cmd/mkversion`
if [ "$1" = "shellvars" ]; then
cat <<EOF

View File

@@ -436,7 +436,7 @@ func (c *Client) ValidateACLJSON(ctx context.Context, source, dest string) (test
}
}()
tests := []ACLTest{ACLTest{User: source, Allow: []string{dest}}}
tests := []ACLTest{{User: source, Allow: []string{dest}}}
postData, err := json.Marshal(tests)
if err != nil {
return nil, err

View File

@@ -63,7 +63,7 @@ func (c *Client) dnsGETRequest(ctx context.Context, endpoint string) ([]byte, er
return b, nil
}
func (c *Client) dnsPOSTRequest(ctx context.Context, endpoint string, postData interface{}) ([]byte, error) {
func (c *Client) dnsPOSTRequest(ctx context.Context, endpoint string, postData any) ([]byte, error) {
path := fmt.Sprintf("%s/api/v2/tailnet/%s/dns/%s", c.baseURL(), c.tailnet, endpoint)
data, err := json.Marshal(&postData)
if err != nil {

View File

@@ -81,7 +81,7 @@ func (m *manualCertManager) TLSConfig() *tls.Config {
return &tls.Config{
Certificates: nil,
NextProtos: []string{
"h2", "http/1.1", // enable HTTP/2
"http/1.1",
},
GetCertificate: m.getCertificate,
}

View File

@@ -3,7 +3,9 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus
filippo.io/edwards25519/field from filippo.io/edwards25519
W 💣 github.com/Microsoft/go-winio from tailscale.com/safesocket
W 💣 github.com/Microsoft/go-winio/internal/fs from github.com/Microsoft/go-winio
W 💣 github.com/Microsoft/go-winio/internal/socket from github.com/Microsoft/go-winio
W github.com/Microsoft/go-winio/internal/stringbuffer from github.com/Microsoft/go-winio/internal/fs
W github.com/Microsoft/go-winio/pkg/guid from github.com/Microsoft/go-winio+
W 💣 github.com/alexbrainman/sspi from github.com/alexbrainman/sspi/internal/common+
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
@@ -13,10 +15,9 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
github.com/fxamacker/cbor/v2 from tailscale.com/tka
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
github.com/golang/protobuf/proto from github.com/matttproud/golang_protobuf_extensions/pbutil+
github.com/golang/protobuf/ptypes/timestamp from github.com/prometheus/client_model/go
github.com/hdevalence/ed25519consensus from tailscale.com/tka
L github.com/josharian/native from github.com/mdlayher/netlink+
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/interfaces
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/interfaces+
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
github.com/klauspost/compress/flate from nhooyr.io/websocket
github.com/matttproud/golang_protobuf_extensions/pbutil from github.com/prometheus/common/expfmt
@@ -64,7 +65,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
google.golang.org/protobuf/runtime/protoiface from github.com/golang/protobuf/proto+
google.golang.org/protobuf/runtime/protoimpl from github.com/golang/protobuf/proto+
google.golang.org/protobuf/types/descriptorpb from google.golang.org/protobuf/reflect/protodesc
google.golang.org/protobuf/types/known/timestamppb from github.com/golang/protobuf/ptypes/timestamp+
google.golang.org/protobuf/types/known/timestamppb from github.com/prometheus/client_golang/prometheus+
nhooyr.io/websocket from tailscale.com/cmd/derper+
nhooyr.io/websocket/internal/errd from nhooyr.io/websocket
nhooyr.io/websocket/internal/xsync from nhooyr.io/websocket
@@ -86,6 +87,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
💣 tailscale.com/net/interfaces from tailscale.com/net/netns+
tailscale.com/net/netaddr from tailscale.com/ipn+
tailscale.com/net/netknob from tailscale.com/net/netns
tailscale.com/net/netmon from tailscale.com/net/sockstats+
tailscale.com/net/netns from tailscale.com/derp/derphttp
tailscale.com/net/netutil from tailscale.com/client/tailscale
tailscale.com/net/packet from tailscale.com/wgengine/filter
@@ -129,7 +131,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
tailscale.com/util/lineread from tailscale.com/hostinfo+
tailscale.com/util/mak from tailscale.com/syncs+
tailscale.com/util/multierr from tailscale.com/health
tailscale.com/util/set from tailscale.com/health
tailscale.com/util/set from tailscale.com/health+
tailscale.com/util/singleflight from tailscale.com/net/dnscache
tailscale.com/util/slicesx from tailscale.com/cmd/derper+
tailscale.com/util/vizerror from tailscale.com/tsweb

177
cmd/equaler/equaler.go Normal file
View File

@@ -0,0 +1,177 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Equaler is a tool to automate the creation of an Equals method.
//
// This tool assumes that if a type you give it contains another named struct
// type, that type will also have an Equal method, and that all fields are
// comparable unless explicitly excluded.
package main
import (
"bytes"
"flag"
"fmt"
"go/token"
"go/types"
"log"
"os"
"strings"
"golang.org/x/exp/slices"
"tailscale.com/util/codegen"
)
var (
flagTypes = flag.String("type", "", "comma-separated list of types; required")
flagBuildTags = flag.String("tags", "", "compiler build tags to apply")
)
func main() {
log.SetFlags(0)
log.SetPrefix("equaler: ")
flag.Parse()
if len(*flagTypes) == 0 {
flag.Usage()
os.Exit(2)
}
typeNames := strings.Split(*flagTypes, ",")
pkg, namedTypes, err := codegen.LoadTypes(*flagBuildTags, ".")
if err != nil {
log.Fatal(err)
}
it := codegen.NewImportTracker(pkg.Types)
buf := new(bytes.Buffer)
for _, typeName := range typeNames {
typ, ok := namedTypes[typeName]
if !ok {
log.Fatalf("could not find type %s", typeName)
}
gen(buf, it, typ, typeNames)
}
cloneOutput := pkg.Name + "_equal.go"
if err := codegen.WritePackageFile("tailscale.com/cmd/equaler", pkg, cloneOutput, it, buf); err != nil {
log.Fatal(err)
}
}
func gen(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, typeNames []string) {
t, ok := typ.Underlying().(*types.Struct)
if !ok {
return
}
name := typ.Obj().Name()
fmt.Fprintf(buf, "// Equal reports whether a and b are equal.\n")
fmt.Fprintf(buf, "func (a *%s) Equal(b *%s) bool {\n", name, name)
writef := func(format string, args ...any) {
fmt.Fprintf(buf, "\t"+format+"\n", args...)
}
writef("if a == b {")
writef("\treturn true")
writef("}")
writef("return a != nil && b != nil &&")
for i := 0; i < t.NumFields(); i++ {
fname := t.Field(i).Name()
ft := t.Field(i).Type()
// Fields which are explicitly ignored are skipped.
if codegen.HasNoEqual(t.Tag(i)) {
writef("\t// Skipping %s because of codegen:noequal", fname)
continue
}
// Fields which are named types that have an Equal() method, get that method used
if named, _ := ft.(*types.Named); named != nil {
if implementsEqual(ft) || slices.Contains(typeNames, named.Obj().Name()) {
writef("\ta.%s.Equal(b.%s) &&", fname, fname)
continue
}
}
// Fields which are just values are directly compared, unless they have an Equal() method.
if !codegen.ContainsPointers(ft) {
writef("\ta.%s == b.%s &&", fname, fname)
continue
}
switch ft := ft.Underlying().(type) {
case *types.Pointer:
if named, _ := ft.Elem().(*types.Named); named != nil {
if slices.Contains(typeNames, named.Obj().Name()) || implementsEqual(ft) {
writef("\t((a.%s == nil) == (b.%s == nil)) && (a.%s == nil || a.%s.Equal(b.%s)) &&", fname, fname, fname, fname, fname)
continue
}
if implementsEqual(ft.Elem()) {
writef("\t((a.%s == nil) == (b.%s == nil)) && (a.%s == nil || a.%s.Equal(*b.%s)) &&", fname, fname, fname, fname, fname)
continue
}
}
if !codegen.ContainsPointers(ft.Elem()) {
writef("\t((a.%s == nil) == (b.%s == nil)) && (a.%s == nil || *a.%s == *b.%s) &&", fname, fname, fname, fname, fname)
continue
}
log.Fatalf("unimplemented: %s (%T)", fname, ft)
case *types.Slice:
// Empty slices and nil slices are different.
writef("\t((a.%s == nil) == (b.%s == nil)) &&", fname, fname)
if named, _ := ft.Elem().(*types.Named); named != nil {
if implementsEqual(ft.Elem()) {
it.Import("golang.org/x/exp/slices")
writef("\tslices.EqualFunc(a.%s, b.%s, func(aa %s, bb %s) bool {return aa.Equal(bb)}) &&", fname, fname, named.Obj().Name(), named.Obj().Name())
continue
}
if slices.Contains(typeNames, named.Obj().Name()) || implementsEqual(types.NewPointer(ft.Elem())) {
it.Import("golang.org/x/exp/slices")
writef("\tslices.EqualFunc(a.%s, b.%s, func(aa %s, bb %s) bool {return aa.Equal(&bb)}) &&", fname, fname, named.Obj().Name(), named.Obj().Name())
continue
}
}
if !codegen.ContainsPointers(ft.Elem()) {
it.Import("golang.org/x/exp/slices")
writef("\tslices.Equal(a.%s, b.%s) &&", fname, fname)
continue
}
log.Fatalf("unimplemented: %s (%T)", fname, ft)
case *types.Map:
if !codegen.ContainsPointers(ft.Elem()) {
it.Import("golang.org/x/exp/maps")
writef("\tmaps.Equal(a.%s, b.%s) &&", fname, fname)
continue
}
log.Fatalf("unimplemented: %s (%T)", fname, ft)
default:
log.Fatalf("unimplemented: %s (%T)", fname, ft)
}
}
writef("\ttrue")
fmt.Fprintf(buf, "}\n\n")
buf.Write(codegen.AssertStructUnchanged(t, name, "Equal", it))
}
// hasBasicUnderlying reports true when typ.Underlying() is a slice or a map.
func hasBasicUnderlying(typ types.Type) bool {
switch typ.Underlying().(type) {
case *types.Slice, *types.Map:
return true
default:
return false
}
}
// implementsEqual reports whether typ has an Equal(typ) bool method.
func implementsEqual(typ types.Type) bool {
return types.Implements(typ, types.NewInterfaceType(
[]*types.Func{types.NewFunc(
token.NoPos, nil, "Equal", types.NewSignatureType(
types.NewVar(token.NoPos, nil, "a", typ),
nil, nil,
types.NewTuple(types.NewVar(token.NoPos, nil, "b", typ)),
types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.Bool])), false))},
[]types.Type{},
))
}

View File

@@ -29,9 +29,9 @@ func main() {
tags := flag.String("tags", "", "comma-separated list of tags to apply to the authkey")
flag.Parse()
clientId := os.Getenv("TS_API_CLIENT_ID")
clientID := os.Getenv("TS_API_CLIENT_ID")
clientSecret := os.Getenv("TS_API_CLIENT_SECRET")
if clientId == "" || clientSecret == "" {
if clientID == "" || clientSecret == "" {
log.Fatal("TS_API_CLIENT_ID and TS_API_CLIENT_SECRET must be set")
}
@@ -39,22 +39,22 @@ func main() {
log.Fatal("at least one tag must be specified")
}
baseUrl := os.Getenv("TS_BASE_URL")
if baseUrl == "" {
baseUrl = "https://api.tailscale.com"
baseURL := os.Getenv("TS_BASE_URL")
if baseURL == "" {
baseURL = "https://api.tailscale.com"
}
credentials := clientcredentials.Config{
ClientID: clientId,
ClientID: clientID,
ClientSecret: clientSecret,
TokenURL: baseUrl + "/api/v2/oauth/token",
TokenURL: baseURL + "/api/v2/oauth/token",
Scopes: []string{"device"},
}
ctx := context.Background()
tsClient := tailscale.NewClient("-", nil)
tsClient.HTTPClient = credentials.Client(ctx)
tsClient.BaseURL = baseUrl
tsClient.BaseURL = baseURL
caps := tailscale.KeyCapabilities{
Devices: tailscale.KeyDeviceCapabilities{

View File

@@ -14,7 +14,6 @@ import (
"go.uber.org/zap"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
@@ -670,11 +669,11 @@ func expectedSTS(stsName, secretName, hostname string) *appsv1.StatefulSet {
},
},
},
Containers: []v1.Container{
Containers: []corev1.Container{
{
Name: "tailscale",
Image: "tailscale/tailscale",
Env: []v1.EnvVar{
Env: []corev1.EnvVar{
{Name: "TS_USERSPACE", Value: "false"},
{Name: "TS_AUTH_ONCE", Value: "true"},
{Name: "TS_DEST_IP", Value: "10.20.30.40"},

View File

@@ -272,7 +272,7 @@ func (p *proxy) serve(sessionID int64, c net.Conn) error {
}
if buf[0] != 'S' {
p.errors.Add("upstream-bad-protocol", 1)
return fmt.Errorf("upstream didn't acknowldge start-ssl, said %q", buf[0])
return fmt.Errorf("upstream didn't acknowledge start-ssl, said %q", buf[0])
}
tlsConf := &tls.Config{
ServerName: p.upstreamHost,

View File

@@ -18,6 +18,7 @@ import (
"golang.org/x/net/dns/dnsmessage"
"inet.af/tcpproxy"
"tailscale.com/client/tailscale"
"tailscale.com/hostinfo"
"tailscale.com/net/netutil"
"tailscale.com/tsnet"
"tailscale.com/types/nettype"
@@ -36,6 +37,8 @@ func main() {
log.Fatal("no ports")
}
hostinfo.SetApp("sniproxy")
var s server
defer s.ts.Close()

View File

@@ -19,6 +19,7 @@ import (
"tailscale.com/envknob"
"tailscale.com/ipn"
"tailscale.com/net/netcheck"
"tailscale.com/net/netmon"
"tailscale.com/net/portmapper"
"tailscale.com/tailcfg"
"tailscale.com/types/logger"
@@ -45,9 +46,14 @@ var netcheckArgs struct {
}
func runNetcheck(ctx context.Context, args []string) error {
logf := logger.WithPrefix(log.Printf, "portmap: ")
netMon, err := netmon.New(logf)
if err != nil {
return err
}
c := &netcheck.Client{
UDPBindAddr: envknob.String("TS_DEBUG_NETCHECK_UDP_BIND"),
PortMapper: portmapper.NewClient(logger.WithPrefix(log.Printf, "portmap: "), nil, nil),
PortMapper: portmapper.NewClient(logf, netMon, nil, nil),
UseDNSCache: false, // always resolve, don't cache
}
if netcheckArgs.verbose {
@@ -97,7 +103,6 @@ func printReport(dm *tailcfg.DERPMap, report *netcheck.Report) error {
var err error
switch netcheckArgs.format {
case "":
break
case "json":
j, err = json.MarshalIndent(report, "", "\t")
case "json-line":

View File

@@ -16,6 +16,7 @@ import (
"path"
"path/filepath"
"reflect"
"runtime"
"sort"
"strconv"
"strings"
@@ -412,6 +413,7 @@ func cleanMountPoint(mount string) (string, error) {
if mount == "" {
return "", errors.New("mount point cannot be empty")
}
mount = cleanMinGWPathConversionIfNeeded(mount)
if !strings.HasPrefix(mount, "/") {
mount = "/" + mount
}
@@ -422,6 +424,26 @@ func cleanMountPoint(mount string) (string, error) {
return "", fmt.Errorf("invalid mount point %q", mount)
}
// cleanMinGWPathConversionIfNeeded strips the EXEPATH prefix from the given
// path if the path is a MinGW(ish) (Windows) shell arg.
//
// MinGW(ish) (Windows) shells perform POSIX-to-Windows path conversion
// converting the leading "/" of any shell arg to the EXEPATH, which mangles the
// mount point. Strip the EXEPATH prefix if it exists. #7963
//
// "/C:/Program Files/Git/foo" -> "/foo"
func cleanMinGWPathConversionIfNeeded(path string) string {
// Only do this on Windows.
if runtime.GOOS != "windows" {
return path
}
if _, ok := os.LookupEnv("MSYSTEM"); ok {
exepath := filepath.ToSlash(os.Getenv("EXEPATH"))
path = strings.TrimPrefix(path, exepath)
}
return path
}
func expandProxyTarget(source string) (string, error) {
if !strings.Contains(source, "://") {
source = "http://" + source

View File

@@ -13,11 +13,13 @@ import (
"fmt"
"log"
"net/netip"
"net/url"
"os"
"os/signal"
"reflect"
"runtime"
"sort"
"strconv"
"strings"
"sync"
"syscall"
@@ -26,6 +28,9 @@ import (
shellquote "github.com/kballard/go-shellquote"
"github.com/peterbourgon/ff/v3/ffcli"
qrcode "github.com/skip2/go-qrcode"
"golang.org/x/oauth2/clientcredentials"
"tailscale.com/client/tailscale"
"tailscale.com/envknob"
"tailscale.com/health/healthmsg"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
@@ -663,6 +668,10 @@ func runUp(ctx context.Context, cmd string, args []string, upArgs upArgsT) (retE
if err != nil {
return err
}
authKey, err = resolveAuthKey(ctx, authKey, upArgs.advertiseTags)
if err != nil {
return err
}
if err := localClient.Start(ctx, ipn.Options{
AuthKey: authKey,
UpdatePrefs: prefs,
@@ -1102,3 +1111,96 @@ func anyPeerAdvertisingRoutes(st *ipnstate.Status) bool {
}
return false
}
func init() {
// Required to use our client API. We're fine with the instability since the
// client lives in the same repo as this code.
tailscale.I_Acknowledge_This_API_Is_Unstable = true
}
// resolveAuthKey either returns v unchanged (in the common case) or, if it
// starts with "tskey-client-" (as Tailscale OAuth secrets do) parses it like
//
// tskey-client-xxxx[?ephemeral=false&bar&preauthorized=BOOL&baseURL=...]
//
// and does the OAuth2 dance to get and return an authkey. The "ephemeral"
// property defaults to true if unspecified. The "preauthorized" defaults to
// false. The "baseURL" defaults to https://api.tailscale.com.
// The passed in tags are required, and must be non-empty. These will be
// set on the authkey generated by the OAuth2 dance.
func resolveAuthKey(ctx context.Context, v, tags string) (string, error) {
if !strings.HasPrefix(v, "tskey-client-") {
return v, nil
}
if !envknob.Bool("TS_EXPERIMENT_OAUTH_AUTHKEY") {
return "", errors.New("oauth authkeys are in experimental status")
}
if tags == "" {
return "", errors.New("oauth authkeys require --advertise-tags")
}
clientSecret, named, _ := strings.Cut(v, "?")
attrs, err := url.ParseQuery(named)
if err != nil {
return "", err
}
for k := range attrs {
switch k {
case "ephemeral", "preauthorized", "baseURL":
default:
return "", fmt.Errorf("unknown attribute %q", k)
}
}
getBool := func(name string, def bool) (bool, error) {
v := attrs.Get(name)
if v == "" {
return def, nil
}
ret, err := strconv.ParseBool(v)
if err != nil {
return false, fmt.Errorf("invalid attribute boolean attribute %s value %q", name, v)
}
return ret, nil
}
ephemeral, err := getBool("ephemeral", true)
if err != nil {
return "", err
}
preauth, err := getBool("preauthorized", false)
if err != nil {
return "", err
}
baseURL := "https://api.tailscale.com"
if v := attrs.Get("baseURL"); v != "" {
baseURL = v
}
credentials := clientcredentials.Config{
ClientID: "some-client-id", // ignored
ClientSecret: clientSecret,
TokenURL: baseURL + "/api/v2/oauth/token",
Scopes: []string{"device"},
}
tsClient := tailscale.NewClient("-", nil)
tsClient.HTTPClient = credentials.Client(ctx)
tsClient.BaseURL = baseURL
caps := tailscale.KeyCapabilities{
Devices: tailscale.KeyDeviceCapabilities{
Create: tailscale.KeyDeviceCreateCapabilities{
Reusable: false,
Ephemeral: ephemeral,
Preauthorized: preauth,
Tags: strings.Split(tags, ","),
},
},
}
authkey, _, err := tsClient.CreateKey(ctx, caps)
if err != nil {
return "", err
}
return authkey, nil
}

View File

@@ -3,7 +3,9 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus
filippo.io/edwards25519/field from filippo.io/edwards25519
W 💣 github.com/Microsoft/go-winio from tailscale.com/safesocket
W 💣 github.com/Microsoft/go-winio/internal/fs from github.com/Microsoft/go-winio
W 💣 github.com/Microsoft/go-winio/internal/socket from github.com/Microsoft/go-winio
W github.com/Microsoft/go-winio/internal/stringbuffer from github.com/Microsoft/go-winio/internal/fs
W github.com/Microsoft/go-winio/pkg/guid from github.com/Microsoft/go-winio+
W 💣 github.com/alexbrainman/sspi from github.com/alexbrainman/sspi/negotiate+
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
@@ -13,7 +15,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
github.com/google/uuid from tailscale.com/util/quarantine+
github.com/hdevalence/ed25519consensus from tailscale.com/tka
L github.com/josharian/native from github.com/mdlayher/netlink+
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/interfaces
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/interfaces+
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
github.com/kballard/go-shellquote from tailscale.com/cmd/tailscale/cli
github.com/klauspost/compress/flate from nhooyr.io/websocket
@@ -74,6 +76,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/net/netcheck from tailscale.com/cmd/tailscale/cli
tailscale.com/net/neterror from tailscale.com/net/netcheck+
tailscale.com/net/netknob from tailscale.com/net/netns
tailscale.com/net/netmon from tailscale.com/net/sockstats+
tailscale.com/net/netns from tailscale.com/derp/derphttp+
tailscale.com/net/netutil from tailscale.com/client/tailscale+
tailscale.com/net/packet from tailscale.com/wgengine/filter+
@@ -151,9 +154,12 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
golang.org/x/net/icmp from tailscale.com/net/ping
golang.org/x/net/idna from golang.org/x/net/http/httpguts+
golang.org/x/net/ipv4 from golang.org/x/net/icmp+
golang.org/x/net/ipv6 from golang.org/x/net/icmp
golang.org/x/net/ipv6 from golang.org/x/net/icmp+
golang.org/x/net/proxy from tailscale.com/net/netns
D golang.org/x/net/route from net+
golang.org/x/oauth2 from golang.org/x/oauth2/clientcredentials
golang.org/x/oauth2/clientcredentials from tailscale.com/cmd/tailscale/cli
golang.org/x/oauth2/internal from golang.org/x/oauth2+
golang.org/x/sync/errgroup from tailscale.com/derp+
golang.org/x/sys/cpu from golang.org/x/crypto/blake2b+
LD golang.org/x/sys/unix from tailscale.com/net/netns+

View File

@@ -23,10 +23,10 @@ import (
"tailscale.com/derp/derphttp"
"tailscale.com/ipn"
"tailscale.com/net/interfaces"
"tailscale.com/net/netmon"
"tailscale.com/net/tshttpproxy"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/wgengine/monitor"
)
var debugArgs struct {
@@ -42,7 +42,7 @@ var debugModeFunc = debugMode // so it can be addressable
func debugMode(args []string) error {
fs := flag.NewFlagSet("debug", flag.ExitOnError)
fs.BoolVar(&debugArgs.ifconfig, "ifconfig", false, "If true, print network interface state")
fs.BoolVar(&debugArgs.monitor, "monitor", false, "If true, run link monitor forever. Precludes all other options.")
fs.BoolVar(&debugArgs.monitor, "monitor", false, "If true, run network monitor forever. Precludes all other options.")
fs.BoolVar(&debugArgs.portmap, "portmap", false, "If true, run portmap debugging. Precludes all other options.")
fs.StringVar(&debugArgs.getURL, "get-url", "", "If non-empty, fetch provided URL.")
fs.StringVar(&debugArgs.derpCheck, "derp", "", "if non-empty, test a DERP ping via named region code")
@@ -76,7 +76,7 @@ func runMonitor(ctx context.Context, loop bool) error {
j, _ := json.MarshalIndent(st, "", " ")
os.Stderr.Write(j)
}
mon, err := monitor.New(log.Printf)
mon, err := netmon.New(log.Printf)
if err != nil {
return err
}
@@ -84,10 +84,10 @@ func runMonitor(ctx context.Context, loop bool) error {
mon.RegisterChangeCallback(func(changed bool, st *interfaces.State) {
if !changed {
log.Printf("Link monitor fired; no change")
log.Printf("Network monitor fired; no change")
return
}
log.Printf("Link monitor fired. New state:")
log.Printf("Network monitor fired. New state:")
dump(st)
})
if loop {
@@ -193,8 +193,8 @@ func checkDerp(ctx context.Context, derpRegion string) (err error) {
priv1 := key.NewNode()
priv2 := key.NewNode()
c1 := derphttp.NewRegionClient(priv1, log.Printf, getRegion)
c2 := derphttp.NewRegionClient(priv2, log.Printf, getRegion)
c1 := derphttp.NewRegionClient(priv1, log.Printf, nil, getRegion)
c2 := derphttp.NewRegionClient(priv2, log.Printf, nil, getRegion)
defer func() {
if err != nil {
c1.Close()

View File

@@ -3,7 +3,9 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus
filippo.io/edwards25519/field from filippo.io/edwards25519
W 💣 github.com/Microsoft/go-winio from tailscale.com/safesocket
W 💣 github.com/Microsoft/go-winio/internal/fs from github.com/Microsoft/go-winio
W 💣 github.com/Microsoft/go-winio/internal/socket from github.com/Microsoft/go-winio
W github.com/Microsoft/go-winio/internal/stringbuffer from github.com/Microsoft/go-winio/internal/fs
W github.com/Microsoft/go-winio/pkg/guid from github.com/Microsoft/go-winio+
W 💣 github.com/alexbrainman/sspi from github.com/alexbrainman/sspi/internal/common+
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
@@ -12,7 +14,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
L github.com/aws/aws-sdk-go-v2 from github.com/aws/aws-sdk-go-v2/internal/ini
L github.com/aws/aws-sdk-go-v2/aws from github.com/aws/aws-sdk-go-v2/aws/middleware+
L github.com/aws/aws-sdk-go-v2/aws/arn from tailscale.com/ipn/store/awsstore
L github.com/aws/aws-sdk-go-v2/aws/defaults from github.com/aws/aws-sdk-go-v2/service/ssm
L github.com/aws/aws-sdk-go-v2/aws/defaults from github.com/aws/aws-sdk-go-v2/service/ssm+
L github.com/aws/aws-sdk-go-v2/aws/middleware from github.com/aws/aws-sdk-go-v2/aws/retry+
L github.com/aws/aws-sdk-go-v2/aws/protocol/query from github.com/aws/aws-sdk-go-v2/service/sts
L github.com/aws/aws-sdk-go-v2/aws/protocol/restjson from github.com/aws/aws-sdk-go-v2/service/ssm+
@@ -38,6 +40,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
L github.com/aws/aws-sdk-go-v2/internal/rand from github.com/aws/aws-sdk-go-v2/aws+
L github.com/aws/aws-sdk-go-v2/internal/sdk from github.com/aws/aws-sdk-go-v2/aws+
L github.com/aws/aws-sdk-go-v2/internal/sdkio from github.com/aws/aws-sdk-go-v2/credentials/processcreds
L github.com/aws/aws-sdk-go-v2/internal/shareddefaults from github.com/aws/aws-sdk-go-v2/config+
L github.com/aws/aws-sdk-go-v2/internal/strings from github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4
L github.com/aws/aws-sdk-go-v2/internal/sync/singleflight from github.com/aws/aws-sdk-go-v2/aws
L github.com/aws/aws-sdk-go-v2/internal/timeconv from github.com/aws/aws-sdk-go-v2/aws/retry
@@ -48,16 +51,19 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
L github.com/aws/aws-sdk-go-v2/service/sso from github.com/aws/aws-sdk-go-v2/config+
L github.com/aws/aws-sdk-go-v2/service/sso/internal/endpoints from github.com/aws/aws-sdk-go-v2/service/sso
L github.com/aws/aws-sdk-go-v2/service/sso/types from github.com/aws/aws-sdk-go-v2/service/sso
L github.com/aws/aws-sdk-go-v2/service/ssooidc from github.com/aws/aws-sdk-go-v2/config+
L github.com/aws/aws-sdk-go-v2/service/ssooidc/internal/endpoints from github.com/aws/aws-sdk-go-v2/service/ssooidc
L github.com/aws/aws-sdk-go-v2/service/ssooidc/types from github.com/aws/aws-sdk-go-v2/service/ssooidc
L github.com/aws/aws-sdk-go-v2/service/sts from github.com/aws/aws-sdk-go-v2/config+
L github.com/aws/aws-sdk-go-v2/service/sts/internal/endpoints from github.com/aws/aws-sdk-go-v2/service/sts
L github.com/aws/aws-sdk-go-v2/service/sts/types from github.com/aws/aws-sdk-go-v2/credentials/stscreds+
L github.com/aws/smithy-go from github.com/aws/aws-sdk-go-v2/aws/protocol/restjson+
L github.com/aws/smithy-go/auth/bearer from github.com/aws/aws-sdk-go-v2/aws
L github.com/aws/smithy-go/auth/bearer from github.com/aws/aws-sdk-go-v2/aws+
L github.com/aws/smithy-go/context from github.com/aws/smithy-go/auth/bearer
L github.com/aws/smithy-go/document from github.com/aws/aws-sdk-go-v2/service/ssm+
L github.com/aws/smithy-go/encoding from github.com/aws/smithy-go/encoding/json+
L github.com/aws/smithy-go/encoding/httpbinding from github.com/aws/aws-sdk-go-v2/aws/protocol/query+
L github.com/aws/smithy-go/encoding/json from github.com/aws/aws-sdk-go-v2/service/ssm
L github.com/aws/smithy-go/encoding/json from github.com/aws/aws-sdk-go-v2/service/ssm+
L github.com/aws/smithy-go/encoding/xml from github.com/aws/aws-sdk-go-v2/service/sts
L github.com/aws/smithy-go/internal/sync/singleflight from github.com/aws/smithy-go/auth/bearer
L github.com/aws/smithy-go/io from github.com/aws/aws-sdk-go-v2/feature/ec2/imds+
@@ -73,6 +79,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
LD 💣 github.com/creack/pty from tailscale.com/ssh/tailssh
W 💣 github.com/dblohm7/wingoes from github.com/dblohm7/wingoes/com
W 💣 github.com/dblohm7/wingoes/com from tailscale.com/cmd/tailscaled
W github.com/dblohm7/wingoes/internal from github.com/dblohm7/wingoes/com
github.com/fxamacker/cbor/v2 from tailscale.com/tka
W 💣 github.com/go-ole/go-ole from github.com/go-ole/go-ole/oleutil+
W 💣 github.com/go-ole/go-ole/oleutil from tailscale.com/wgengine/winnet
@@ -93,7 +100,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
github.com/klauspost/compress/flate from nhooyr.io/websocket
github.com/klauspost/compress/fse from github.com/klauspost/compress/huff0
github.com/klauspost/compress/huff0 from github.com/klauspost/compress/zstd
github.com/klauspost/compress/internal/cpuinfo from github.com/klauspost/compress/zstd
github.com/klauspost/compress/internal/cpuinfo from github.com/klauspost/compress/zstd+
github.com/klauspost/compress/internal/snapref from github.com/klauspost/compress/zstd
github.com/klauspost/compress/zstd from tailscale.com/smallzstd
github.com/klauspost/compress/zstd/internal/xxhash from github.com/klauspost/compress/zstd
@@ -105,12 +112,17 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
L github.com/mdlayher/sdnotify from tailscale.com/util/systemd
L 💣 github.com/mdlayher/socket from github.com/mdlayher/netlink
💣 github.com/mitchellh/go-ps from tailscale.com/safesocket
L github.com/pierrec/lz4/v4 from github.com/u-root/uio/uio
L github.com/pierrec/lz4/v4/internal/lz4block from github.com/pierrec/lz4/v4+
L github.com/pierrec/lz4/v4/internal/lz4errors from github.com/pierrec/lz4/v4+
L github.com/pierrec/lz4/v4/internal/lz4stream from github.com/pierrec/lz4/v4
L github.com/pierrec/lz4/v4/internal/xxh32 from github.com/pierrec/lz4/v4/internal/lz4stream
W github.com/pkg/errors from github.com/tailscale/certstore
LD github.com/pkg/sftp from tailscale.com/ssh/tailssh
LD github.com/pkg/sftp/internal/encoding/ssh/filexfer from github.com/pkg/sftp
W 💣 github.com/tailscale/certstore from tailscale.com/control/controlclient
LD github.com/tailscale/golang-x-crypto/chacha20 from github.com/tailscale/golang-x-crypto/ssh
LD 💣 github.com/tailscale/golang-x-crypto/internal/subtle from github.com/tailscale/golang-x-crypto/chacha20
LD 💣 github.com/tailscale/golang-x-crypto/internal/alias from github.com/tailscale/golang-x-crypto/chacha20
LD github.com/tailscale/golang-x-crypto/ssh from tailscale.com/ipn/ipnlocal+
LD github.com/tailscale/golang-x-crypto/ssh/internal/bcrypt_pbkdf from github.com/tailscale/golang-x-crypto/ssh
github.com/tailscale/goupnp from github.com/tailscale/goupnp/dcps/internetgateway2+
@@ -240,11 +252,12 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/net/netcheck from tailscale.com/wgengine/magicsock
tailscale.com/net/neterror from tailscale.com/net/dns/resolver+
tailscale.com/net/netknob from tailscale.com/net/netns+
tailscale.com/net/netmon from tailscale.com/cmd/tailscaled+
tailscale.com/net/netns from tailscale.com/derp/derphttp+
💣 tailscale.com/net/netstat from tailscale.com/ipn/ipnauth+
tailscale.com/net/netutil from tailscale.com/ipn/ipnlocal+
tailscale.com/net/packet from tailscale.com/net/tstun+
tailscale.com/net/ping from tailscale.com/net/netcheck
tailscale.com/net/ping from tailscale.com/net/netcheck+
tailscale.com/net/portmapper from tailscale.com/net/netcheck+
tailscale.com/net/proxymux from tailscale.com/cmd/tailscaled
tailscale.com/net/routetable from tailscale.com/doctor/routetable
@@ -324,7 +337,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/wgengine/capture from tailscale.com/ipn/ipnlocal+
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
tailscale.com/wgengine/monitor from tailscale.com/control/controlclient+
tailscale.com/wgengine/netlog from tailscale.com/wgengine
tailscale.com/wgengine/netstack from tailscale.com/cmd/tailscaled
tailscale.com/wgengine/router from tailscale.com/ipn/ipnlocal+

View File

@@ -39,6 +39,7 @@ import (
"tailscale.com/logtail"
"tailscale.com/net/dns"
"tailscale.com/net/dnsfallback"
"tailscale.com/net/netmon"
"tailscale.com/net/netns"
"tailscale.com/net/proxymux"
"tailscale.com/net/socks5"
@@ -59,7 +60,6 @@ import (
"tailscale.com/version"
"tailscale.com/version/distro"
"tailscale.com/wgengine"
"tailscale.com/wgengine/monitor"
"tailscale.com/wgengine/netstack"
"tailscale.com/wgengine/router"
)
@@ -329,7 +329,15 @@ var logPol *logpolicy.Policy
var debugMux *http.ServeMux
func run() error {
pol := logpolicy.New(logtail.CollectionNode)
var logf logger.Logf = log.Printf
netMon, err := netmon.New(func(format string, args ...any) {
logf(format, args...)
})
if err != nil {
return fmt.Errorf("netmon.New: %w", err)
}
pol := logpolicy.New(logtail.CollectionNode, netMon)
pol.SetVerbosityLevel(args.verbose)
logPol = pol
defer func() {
@@ -353,7 +361,6 @@ func run() error {
return nil
}
var logf logger.Logf = log.Printf
if envknob.Bool("TS_DEBUG_MEMORY") {
logf = logger.RusagePrefixLog(logf)
}
@@ -379,10 +386,10 @@ func run() error {
debugMux = newDebugMux()
}
return startIPNServer(context.Background(), logf, pol.PublicID)
return startIPNServer(context.Background(), logf, pol.PublicID, netMon)
}
func startIPNServer(ctx context.Context, logf logger.Logf, logID logid.PublicID) error {
func startIPNServer(ctx context.Context, logf logger.Logf, logID logid.PublicID, netMon *netmon.Monitor) error {
ln, err := safesocket.Listen(args.socketpath)
if err != nil {
return fmt.Errorf("safesocket.Listen: %v", err)
@@ -408,7 +415,7 @@ func startIPNServer(ctx context.Context, logf logger.Logf, logID logid.PublicID)
}
}()
srv := ipnserver.New(logf, logID)
srv := ipnserver.New(logf, logID, netMon)
if debugMux != nil {
debugMux.HandleFunc("/debug/ipn", srv.ServeHTMLStatus)
}
@@ -426,7 +433,7 @@ func startIPNServer(ctx context.Context, logf logger.Logf, logID logid.PublicID)
return
}
}
lb, err := getLocalBackend(ctx, logf, logID)
lb, err := getLocalBackend(ctx, logf, logID, netMon)
if err == nil {
logf("got LocalBackend in %v", time.Since(t0).Round(time.Millisecond))
srv.SetLocalBackend(lb)
@@ -450,19 +457,15 @@ func startIPNServer(ctx context.Context, logf logger.Logf, logID logid.PublicID)
return nil
}
func getLocalBackend(ctx context.Context, logf logger.Logf, logID logid.PublicID) (_ *ipnlocal.LocalBackend, retErr error) {
linkMon, err := monitor.New(logf)
if err != nil {
return nil, fmt.Errorf("monitor.New: %w", err)
}
func getLocalBackend(ctx context.Context, logf logger.Logf, logID logid.PublicID, netMon *netmon.Monitor) (_ *ipnlocal.LocalBackend, retErr error) {
if logPol != nil {
logPol.Logtail.SetLinkMonitor(linkMon)
logPol.Logtail.SetNetMon(netMon)
}
socksListener, httpProxyListener := mustStartProxyListeners(args.socksAddr, args.httpProxyAddr)
dialer := &tsdial.Dialer{Logf: logf} // mutated below (before used)
e, onlyNetstack, err := createEngine(logf, linkMon, dialer)
e, onlyNetstack, err := createEngine(logf, netMon, dialer)
if err != nil {
return nil, fmt.Errorf("createEngine: %w", err)
}
@@ -534,7 +537,7 @@ func getLocalBackend(ctx context.Context, logf logger.Logf, logID logid.PublicID
lb.SetLogFlusher(logPol.Logtail.StartFlush)
}
if root := lb.TailscaleVarRoot(); root != "" {
dnsfallback.SetCachePath(filepath.Join(root, "derpmap.cached.json"))
dnsfallback.SetCachePath(filepath.Join(root, "derpmap.cached.json"), logf)
}
lb.SetDecompressor(func() (controlclient.Decompressor, error) {
return smallzstd.NewDecoder(nil)
@@ -551,14 +554,14 @@ func getLocalBackend(ctx context.Context, logf logger.Logf, logID logid.PublicID
//
// onlyNetstack is true if the user has explicitly requested that we use netstack
// for all networking.
func createEngine(logf logger.Logf, linkMon *monitor.Mon, dialer *tsdial.Dialer) (e wgengine.Engine, onlyNetstack bool, err error) {
func createEngine(logf logger.Logf, netMon *netmon.Monitor, dialer *tsdial.Dialer) (e wgengine.Engine, onlyNetstack bool, err error) {
if args.tunname == "" {
return nil, false, errors.New("no --tun value specified")
}
var errs []error
for _, name := range strings.Split(args.tunname, ",") {
logf("wgengine.NewUserspaceEngine(tun %q) ...", name)
e, onlyNetstack, err = tryEngine(logf, linkMon, dialer, name)
e, onlyNetstack, err = tryEngine(logf, netMon, dialer, name)
if err == nil {
return e, onlyNetstack, nil
}
@@ -590,11 +593,11 @@ func handleSubnetsInNetstack() bool {
var tstunNew = tstun.New
func tryEngine(logf logger.Logf, linkMon *monitor.Mon, dialer *tsdial.Dialer, name string) (e wgengine.Engine, onlyNetstack bool, err error) {
func tryEngine(logf logger.Logf, netMon *netmon.Monitor, dialer *tsdial.Dialer, name string) (e wgengine.Engine, onlyNetstack bool, err error) {
conf := wgengine.Config{
ListenPort: args.port,
LinkMonitor: linkMon,
Dialer: dialer,
ListenPort: args.port,
NetMon: netMon,
Dialer: dialer,
}
onlyNetstack = name == "userspace-networking"
@@ -633,7 +636,7 @@ func tryEngine(logf logger.Logf, linkMon *monitor.Mon, dialer *tsdial.Dialer, na
return e, false, err
}
r, err := router.New(logf, dev, linkMon)
r, err := router.New(logf, dev, netMon)
if err != nil {
dev.Close()
return nil, false, fmt.Errorf("creating router: %w", err)

View File

@@ -45,6 +45,7 @@ import (
"tailscale.com/logpolicy"
"tailscale.com/logtail/backoff"
"tailscale.com/net/dns"
"tailscale.com/net/netmon"
"tailscale.com/net/tstun"
"tailscale.com/types/logger"
"tailscale.com/types/logid"
@@ -291,8 +292,13 @@ func beWindowsSubprocess() bool {
}
}()
netMon, err := netmon.New(log.Printf)
if err != nil {
log.Printf("Could not create netMon: %v", err)
netMon = nil
}
publicLogID, _ := logid.ParsePublicID(logID)
err := startIPNServer(ctx, log.Printf, publicLogID)
err = startIPNServer(ctx, log.Printf, publicLogID, netMon)
if err != nil {
log.Fatalf("ipnserver: %v", err)
}

View File

@@ -46,7 +46,7 @@ import (
var ControlURL = ipn.DefaultControlURL
func main() {
js.Global().Set("newIPN", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
js.Global().Set("newIPN", js.FuncOf(func(this js.Value, args []js.Value) any {
if len(args) != 1 {
log.Fatal("Usage: newIPN(config)")
return nil
@@ -123,7 +123,7 @@ func newIPN(jsConfig js.Value) map[string]any {
}
logid := lpc.PublicID
srv := ipnserver.New(logf, logid)
srv := ipnserver.New(logf, logid, nil /* no netMon */)
lb, err := ipnlocal.NewLocalBackend(logf, logid, store, dialer, eng, controlclient.LoginEphemeral)
if err != nil {
log.Fatalf("ipnlocal.NewLocalBackend: %v", err)
@@ -146,7 +146,7 @@ func newIPN(jsConfig js.Value) map[string]any {
}
return map[string]any{
"run": js.FuncOf(func(this js.Value, args []js.Value) interface{} {
"run": js.FuncOf(func(this js.Value, args []js.Value) any {
if len(args) != 1 {
log.Fatal(`Usage: run({
notifyState(state: int): void,
@@ -159,7 +159,7 @@ func newIPN(jsConfig js.Value) map[string]any {
jsIPN.run(args[0])
return nil
}),
"login": js.FuncOf(func(this js.Value, args []js.Value) interface{} {
"login": js.FuncOf(func(this js.Value, args []js.Value) any {
if len(args) != 0 {
log.Printf("Usage: login()")
return nil
@@ -167,7 +167,7 @@ func newIPN(jsConfig js.Value) map[string]any {
jsIPN.login()
return nil
}),
"logout": js.FuncOf(func(this js.Value, args []js.Value) interface{} {
"logout": js.FuncOf(func(this js.Value, args []js.Value) any {
if len(args) != 0 {
log.Printf("Usage: logout()")
return nil
@@ -175,7 +175,7 @@ func newIPN(jsConfig js.Value) map[string]any {
jsIPN.logout()
return nil
}),
"ssh": js.FuncOf(func(this js.Value, args []js.Value) interface{} {
"ssh": js.FuncOf(func(this js.Value, args []js.Value) any {
if len(args) != 3 {
log.Printf("Usage: ssh(hostname, userName, termConfig)")
return nil
@@ -185,7 +185,7 @@ func newIPN(jsConfig js.Value) map[string]any {
args[1].String(),
args[2])
}),
"fetch": js.FuncOf(func(this js.Value, args []js.Value) interface{} {
"fetch": js.FuncOf(func(this js.Value, args []js.Value) any {
if len(args) != 1 {
log.Printf("Usage: fetch(url)")
return nil
@@ -334,10 +334,10 @@ func (i *jsIPN) ssh(host, username string, termConfig js.Value) map[string]any {
go jsSSHSession.Run()
return map[string]any{
"close": js.FuncOf(func(this js.Value, args []js.Value) interface{} {
"close": js.FuncOf(func(this js.Value, args []js.Value) any {
return jsSSHSession.Close() != nil
}),
"resize": js.FuncOf(func(this js.Value, args []js.Value) interface{} {
"resize": js.FuncOf(func(this js.Value, args []js.Value) any {
rows := args[0].Int()
cols := args[1].Int()
return jsSSHSession.Resize(rows, cols) != nil
@@ -426,7 +426,7 @@ func (s *jsSSHSession) Run() {
session.Stdout = termWriter{writeFn}
session.Stderr = termWriter{writeFn}
setReadFn.Invoke(js.FuncOf(func(this js.Value, args []js.Value) interface{} {
setReadFn.Invoke(js.FuncOf(func(this js.Value, args []js.Value) any {
input := args[0].String()
_, err := stdin.Write([]byte(input))
if err != nil {
@@ -496,7 +496,7 @@ func (i *jsIPN) fetch(url string) js.Value {
return map[string]any{
"status": res.StatusCode,
"statusText": res.Status,
"text": js.FuncOf(func(this js.Value, args []js.Value) interface{} {
"text": js.FuncOf(func(this js.Value, args []js.Value) any {
return makePromise(func() (any, error) {
defer res.Body.Close()
buf := new(bytes.Buffer)
@@ -602,7 +602,7 @@ func generateHostname() string {
// f is run on a goroutine and its return value is used to resolve the promise
// (or reject it if an error is returned).
func makePromise(f func() (any, error)) js.Value {
handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
handler := js.FuncOf(func(this js.Value, args []js.Value) any {
resolve := args[0]
reject := args[1]
go func() {

View File

@@ -398,7 +398,7 @@ type maxMsgBuffer [maxMessageSize]byte
// bufPool holds the temporary buffers for Conn.Read & Write.
var bufPool = &sync.Pool{
New: func() interface{} {
New: func() any {
return new(maxMsgBuffer)
},
}

View File

@@ -37,6 +37,7 @@ import (
"tailscale.com/net/dnscache"
"tailscale.com/net/dnsfallback"
"tailscale.com/net/interfaces"
"tailscale.com/net/netmon"
"tailscale.com/net/netutil"
"tailscale.com/net/tlsdial"
"tailscale.com/net/tsdial"
@@ -54,20 +55,20 @@ import (
"tailscale.com/util/multierr"
"tailscale.com/util/singleflight"
"tailscale.com/util/systemd"
"tailscale.com/wgengine/monitor"
)
// Direct is the client that connects to a tailcontrol server for a node.
type Direct struct {
httpc *http.Client // HTTP client used to talk to tailcontrol
dialer *tsdial.Dialer
dnsCache *dnscache.Resolver
serverURL string // URL of the tailcontrol server
timeNow func() time.Time
lastPrintMap time.Time
newDecompressor func() (Decompressor, error)
keepAlive bool
logf logger.Logf
linkMon *monitor.Mon // or nil
netMon *netmon.Monitor // or nil
discoPubKey key.DiscoPublic
getMachinePrivKey func() (key.MachinePrivate, error)
debugFlags []string
@@ -113,7 +114,7 @@ type Options struct {
HTTPTestClient *http.Client // optional HTTP client to use (for tests only)
NoiseTestClient *http.Client // optional HTTP client to use for noise RPCs (tests only)
DebugFlags []string // debug settings to send to control
LinkMonitor *monitor.Mon // optional link monitor
NetMon *netmon.Monitor // optional network monitor
PopBrowserURL func(url string) // optional func to open browser
OnClientVersion func(*tailcfg.ClientVersion) // optional func to inform GUI of client version status
OnControlTime func(time.Time) // optional func to notify callers of new time from control
@@ -199,6 +200,14 @@ func NewDirect(opts Options) (*Direct, error) {
opts.Logf = log.Printf
}
dnsCache := &dnscache.Resolver{
Forward: dnscache.Get().Forward, // use default cache's forwarder
UseLastGood: true,
LookupIPFallback: dnsfallback.MakeLookupFunc(opts.Logf, opts.NetMon),
Logf: opts.Logf,
NetMon: opts.NetMon,
}
httpc := opts.HTTPTestClient
if httpc == nil && runtime.GOOS == "js" {
// In js/wasm, net/http.Transport (as of Go 1.18) will
@@ -208,12 +217,6 @@ func NewDirect(opts Options) (*Direct, error) {
httpc = http.DefaultClient
}
if httpc == nil {
dnsCache := &dnscache.Resolver{
Forward: dnscache.Get().Forward, // use default cache's forwarder
UseLastGood: true,
LookupIPFallback: dnsfallback.Lookup,
Logf: opts.Logf,
}
tr := http.DefaultTransport.(*http.Transport).Clone()
tr.Proxy = tshttpproxy.ProxyFromEnvironment
tshttpproxy.SetTransportGetProxyConnectHeader(tr)
@@ -241,7 +244,7 @@ func NewDirect(opts Options) (*Direct, error) {
discoPubKey: opts.DiscoPublicKey,
debugFlags: opts.DebugFlags,
keepSharerAndUserSplit: opts.KeepSharerAndUserSplit,
linkMon: opts.LinkMonitor,
netMon: opts.NetMon,
skipIPForwardingCheck: opts.SkipIPForwardingCheck,
pinger: opts.Pinger,
popBrowser: opts.PopBrowserURL,
@@ -249,6 +252,7 @@ func NewDirect(opts Options) (*Direct, error) {
onControlTime: opts.OnControlTime,
c2nHandler: opts.C2NHandler,
dialer: opts.Dialer,
dnsCache: dnsCache,
dialPlan: opts.DialPlan,
}
if opts.Hostinfo == nil {
@@ -871,8 +875,8 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, readOnly bool
ReadOnly: readOnly && !allowStream,
}
var extraDebugFlags []string
if hi != nil && c.linkMon != nil && !c.skipIPForwardingCheck &&
ipForwardingBroken(hi.RoutableIPs, c.linkMon.InterfaceState()) {
if hi != nil && c.netMon != nil && !c.skipIPForwardingCheck &&
ipForwardingBroken(hi.RoutableIPs, c.netMon.InterfaceState()) {
extraDebugFlags = append(extraDebugFlags, "warn-ip-forwarding-off")
}
if health.RouterHealth() != nil {
@@ -1508,7 +1512,16 @@ func (c *Direct) getNoiseClient() (*NoiseClient, error) {
return nil, err
}
c.logf("creating new noise client")
nc, err := NewNoiseClient(k, serverNoiseKey, c.serverURL, c.dialer, dp)
nc, err := NewNoiseClient(NoiseOpts{
PrivKey: k,
ServerPubKey: serverNoiseKey,
ServerURL: c.serverURL,
Dialer: c.dialer,
DNSCache: c.dnsCache,
Logf: c.logf,
NetMon: c.netMon,
DialPlan: dp,
})
if err != nil {
return nil, err
}

View File

@@ -19,9 +19,12 @@ import (
"golang.org/x/net/http2"
"tailscale.com/control/controlbase"
"tailscale.com/control/controlhttp"
"tailscale.com/net/dnscache"
"tailscale.com/net/netmon"
"tailscale.com/net/tsdial"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/logger"
"tailscale.com/util/mak"
"tailscale.com/util/multierr"
"tailscale.com/util/singleflight"
@@ -156,6 +159,7 @@ type NoiseClient struct {
sfDial singleflight.Group[struct{}, *noiseConn]
dialer *tsdial.Dialer
dnsCache *dnscache.Resolver
privKey key.MachinePrivate
serverPubKey key.MachinePublic
host string // the host part of serverURL
@@ -167,6 +171,9 @@ type NoiseClient struct {
// be nil.
dialPlan func() *tailcfg.ControlDialPlan
logf logger.Logf
netMon *netmon.Monitor
// mu only protects the following variables.
mu sync.Mutex
last *noiseConn // or nil
@@ -174,12 +181,39 @@ type NoiseClient struct {
connPool map[int]*noiseConn // active connections not yet closed; see noiseConn.Close
}
// NoiseOpts contains options for the NewNoiseClient function. All fields are
// required unless otherwise specified.
type NoiseOpts struct {
// PrivKey is this node's private key.
PrivKey key.MachinePrivate
// ServerPubKey is the public key of the server.
ServerPubKey key.MachinePublic
// ServerURL is the URL of the server to connect to.
ServerURL string
// Dialer's SystemDial function is used to connect to the server.
Dialer *tsdial.Dialer
// DNSCache is the caching Resolver to use to connect to the server.
//
// This field can be nil.
DNSCache *dnscache.Resolver
// Logf is the log function to use. This field can be nil.
Logf logger.Logf
// NetMon is the network monitor that, if set, will be used to get the
// network interface state. This field can be nil; if so, the current
// state will be looked up dynamically.
NetMon *netmon.Monitor
// DialPlan, if set, is a function that should return an explicit plan
// on how to connect to the server.
DialPlan func() *tailcfg.ControlDialPlan
}
// NewNoiseClient returns a new noiseClient for the provided server and machine key.
// serverURL is of the form https://<host>:<port> (no trailing slash).
//
// netMon may be nil, if non-nil it's used to do faster interface lookups.
// dialPlan may be nil
func NewNoiseClient(privKey key.MachinePrivate, serverPubKey key.MachinePublic, serverURL string, dialer *tsdial.Dialer, dialPlan func() *tailcfg.ControlDialPlan) (*NoiseClient, error) {
u, err := url.Parse(serverURL)
func NewNoiseClient(opts NoiseOpts) (*NoiseClient, error) {
u, err := url.Parse(opts.ServerURL)
if err != nil {
return nil, err
}
@@ -199,14 +233,18 @@ func NewNoiseClient(privKey key.MachinePrivate, serverPubKey key.MachinePublic,
httpPort = "80"
httpsPort = "443"
}
np := &NoiseClient{
serverPubKey: serverPubKey,
privKey: privKey,
serverPubKey: opts.ServerPubKey,
privKey: opts.PrivKey,
host: u.Hostname(),
httpPort: httpPort,
httpsPort: httpsPort,
dialer: dialer,
dialPlan: dialPlan,
dialer: opts.Dialer,
dnsCache: opts.DNSCache,
dialPlan: opts.DialPlan,
logf: opts.Logf,
netMon: opts.NetMon,
}
// Create the HTTP/2 Transport using a net/http.Transport
@@ -365,7 +403,10 @@ func (nc *NoiseClient) dial() (*noiseConn, error) {
ControlKey: nc.serverPubKey,
ProtocolVersion: uint16(tailcfg.CurrentCapabilityVersion),
Dialer: nc.dialer.SystemDial,
DNSCache: nc.dnsCache,
DialPlan: dialPlan,
Logf: nc.logf,
NetMon: nc.netMon,
}).Dial(ctx)
if err != nil {
return nil, err

View File

@@ -74,7 +74,12 @@ func (tt noiseClientTest) run(t *testing.T) {
defer hs.Close()
dialer := new(tsdial.Dialer)
nc, err := NewNoiseClient(clientPrivate, serverPrivate.Public(), hs.URL, dialer, nil)
nc, err := NewNoiseClient(NoiseOpts{
PrivKey: clientPrivate,
ServerPubKey: serverPrivate.Public(),
ServerURL: hs.URL,
Dialer: dialer,
})
if err != nil {
t.Fatal(err)
}

View File

@@ -374,6 +374,22 @@ func (a *Dialer) dialURL(ctx context.Context, u *url.URL, addr netip.Addr) (*Cli
}, nil
}
// resolver returns a.DNSCache if non-nil or a new *dnscache.Resolver
// otherwise.
func (a *Dialer) resolver() *dnscache.Resolver {
if a.DNSCache != nil {
return a.DNSCache
}
return &dnscache.Resolver{
Forward: dnscache.Get().Forward,
LookupIPFallback: dnsfallback.MakeLookupFunc(a.logf, a.NetMon),
UseLastGood: true,
Logf: a.Logf, // not a.logf method; we want to propagate nil-ness
NetMon: a.NetMon,
}
}
// tryURLUpgrade connects to u, and tries to upgrade it to a net.Conn. If addr
// is valid, then no DNS is used and the connection will be made to the
// provided address.
@@ -389,14 +405,10 @@ func (a *Dialer) tryURLUpgrade(ctx context.Context, u *url.URL, addr netip.Addr,
SingleHostStaticResult: []netip.Addr{addr},
SingleHost: u.Hostname(),
Logf: a.Logf, // not a.logf method; we want to propagate nil-ness
NetMon: a.NetMon,
}
} else {
dns = &dnscache.Resolver{
Forward: dnscache.Get().Forward,
LookupIPFallback: dnsfallback.Lookup,
UseLastGood: true,
Logf: a.Logf, // not a.logf method; we want to propagate nil-ness
}
dns = a.resolver()
}
var dialer dnscache.DialContextFunc

View File

@@ -9,6 +9,7 @@ import (
"time"
"tailscale.com/net/dnscache"
"tailscale.com/net/netmon"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/logger"
@@ -66,10 +67,17 @@ type Dialer struct {
// If not specified, this defaults to net.Dialer.DialContext.
Dialer dnscache.DialContextFunc
// DNSCache is the caching Resolver used by this Dialer.
//
// If not specified, a new Resolver is created per attempt.
DNSCache *dnscache.Resolver
// Logf, if set, is a logging function to use; if unset, logs are
// dropped.
Logf logger.Logf
NetMon *netmon.Monitor
// DialPlan, if set, contains instructions from the control server on
// how to connect to it. If present, we will try the methods in this
// plan before falling back to DNS.

61
derp/README.md Normal file
View File

@@ -0,0 +1,61 @@
# DERP
This directory (and subdirectories) contain the DERP code. The server itself is
in `../cmd/derper`.
DERP is a packet relay system (client and servers) where peers are addressed
using WireGuard public keys instead of IP addresses.
It relays two types of packets:
* "Disco" discovery messages (see `../disco`) as the a side channel during [NAT
traversal](https://tailscale.com/blog/how-nat-traversal-works/).
* Encrypted WireGuard packets as the fallback of last resort when UDP is blocked
or NAT traversal fails.
## DERP Map
Each client receives a "[DERP
Map](https://pkg.go.dev/tailscale.com/tailcfg#DERPMap)" from the coordination
server describing the DERP servers the client should try to use.
The client picks its home "DERP home" based on latency. This is done to keep
costs low by avoid using cloud load balancers (pricey) or anycast, which would
necessarily require server-side routing between DERP regions.
Clients pick their DERP home and report it to the coordination server which
shares it to all the peers in the tailnet. When a peer wants to send a packet
and it doesn't already have a WireGuard session open, it sends disco messages
(some direct, and some over DERP), trying to do the NAT traversal. The client
will make connections to multiple DERP regions as needed. Only the DERP home
region connection needs to be alive forever.
## DERP Regions
Tailscale runs 1 or more DERP nodes (instances of `cmd/derper`) in various
geographic regions to make sure users have low latency to their DERP home.
Regions generally have multiple nodes per region "meshed" (routing to each
other) together for redundancy: it allows for cloud failures or upgrades without
kicking users out to a higher latency region. Instead, clients will reconnect to
the next node in the region. Each node in the region is required to to be meshed
with every other node in the region and forward packets to the other nodes in
the region. Packets are forwarded only one hop within the region. There is no
routing between regions. The assumption is that the mesh TCP connections are
over a VPC that's very fast, low latency, and not charged per byte. The
coordination server assigns the list of nodes in a region as a function of the
tailnet, so all nodes within a tailnet should generally be on the same node and
not require forwarding. Only after a failure do clients of a particular tailnet
get split between nodes in a region and require inter-node forwarding. But over
time it balances back out. There's also an admin-only DERP frame type to force
close the TCP connection of a particular client to force them to reconnect to
their primary if the operator wants to force things to balance out sooner.
(Using the `(*derphttp.Client).ClosePeer` method, as used by Tailscale's
internal rarely-used `cmd/derpprune` maintenance tool)
We generally run a minimum of three nodes in a region not for quorum reasons
(there's no voting) but just because two is too uncomfortably few for cascading
failure reasons: if you're running two nodes at 51% load (CPU, memory, etc) and
then one fails, that makes the second one fail. With three or more nodes, you
can run each node a bit hotter.

View File

@@ -31,6 +31,7 @@ import (
"tailscale.com/derp"
"tailscale.com/envknob"
"tailscale.com/net/dnscache"
"tailscale.com/net/netmon"
"tailscale.com/net/netns"
"tailscale.com/net/sockstats"
"tailscale.com/net/tlsdial"
@@ -55,6 +56,7 @@ type Client struct {
privateKey key.NodePrivate
logf logger.Logf
netMon *netmon.Monitor // optional; nil means interfaces will be looked up on-demand
dialer func(ctx context.Context, network, addr string) (net.Conn, error)
// Either url or getRegion is non-nil:
@@ -88,11 +90,13 @@ func (c *Client) String() string {
// NewRegionClient returns a new DERP-over-HTTP client. It connects lazily.
// To trigger a connection, use Connect.
func NewRegionClient(privateKey key.NodePrivate, logf logger.Logf, getRegion func() *tailcfg.DERPRegion) *Client {
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
func NewRegionClient(privateKey key.NodePrivate, logf logger.Logf, netMon *netmon.Monitor, getRegion func() *tailcfg.DERPRegion) *Client {
ctx, cancel := context.WithCancel(context.Background())
c := &Client{
privateKey: privateKey,
logf: logf,
netMon: netMon,
getRegion: getRegion,
ctx: ctx,
cancelCtx: cancel,
@@ -492,7 +496,7 @@ func (c *Client) dialURL(ctx context.Context) (net.Conn, error) {
return c.dialer(ctx, "tcp", net.JoinHostPort(host, urlPort(c.url)))
}
hostOrIP := host
dialer := netns.NewDialer(c.logf)
dialer := netns.NewDialer(c.logf, c.netMon)
if c.DNSCache != nil {
ip, _, _, err := c.DNSCache.LookupIP(ctx, host)
@@ -587,7 +591,7 @@ func (c *Client) DialRegionTLS(ctx context.Context, reg *tailcfg.DERPRegion) (tl
}
func (c *Client) dialContext(ctx context.Context, proto, addr string) (net.Conn, error) {
return netns.NewDialer(c.logf).DialContext(ctx, proto, addr)
return netns.NewDialer(c.logf, c.netMon).DialContext(ctx, proto, addr)
}
// shouldDialProto reports whether an explicitly provided IPv4 or IPv6

View File

@@ -115,4 +115,4 @@
in
flake-utils.lib.eachDefaultSystem (system: flakeForSystem nixpkgs system);
}
# nix-direnv cache busting line: sha256-LfHkNXQbg7/u6y882gtINuQtwinYakg3abKJTDrrADo=
# nix-direnv cache busting line: sha256-ZQ6aE+9PfAxfeNQeDzwcOCXpztLORVriHkEw51lbeHM=

406
go.mod
View File

@@ -3,92 +3,93 @@ module tailscale.com
go 1.20
require (
filippo.io/mkcert v1.4.3
github.com/Microsoft/go-winio v0.6.0
filippo.io/mkcert v1.4.4
github.com/Microsoft/go-winio v0.6.1
github.com/akutz/memconn v0.1.0
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74
github.com/andybalholm/brotli v1.0.3
github.com/andybalholm/brotli v1.0.5
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
github.com/aws/aws-sdk-go-v2 v1.17.3
github.com/aws/aws-sdk-go-v2/config v1.11.0
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.7.4
github.com/aws/aws-sdk-go-v2/service/s3 v1.21.0
github.com/aws/aws-sdk-go-v2/service/ssm v1.35.0
github.com/aws/aws-sdk-go-v2 v1.18.0
github.com/aws/aws-sdk-go-v2/config v1.18.22
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.64
github.com/aws/aws-sdk-go-v2/service/s3 v1.33.0
github.com/aws/aws-sdk-go-v2/service/ssm v1.36.3
github.com/coreos/go-iptables v0.6.0
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/creack/pty v1.1.17
github.com/dave/jennifer v1.4.1
github.com/dblohm7/wingoes v0.0.0-20221124203957-6ac47ab19aa5
github.com/creack/pty v1.1.18
github.com/dave/jennifer v1.6.1
github.com/dblohm7/wingoes v0.0.0-20230426155039-111c8c3b57c8
github.com/dsnet/try v0.0.3
github.com/evanw/esbuild v0.14.53
github.com/frankban/quicktest v1.14.0
github.com/frankban/quicktest v1.14.5
github.com/fxamacker/cbor/v2 v2.4.0
github.com/go-json-experiment/json v0.0.0-20221017203807-c5ed296b8c92
github.com/go-json-experiment/json v0.0.0-20230321051131-ccbac49a6929
github.com/go-logr/zapr v1.2.3
github.com/go-ole/go-ole v1.2.6
github.com/godbus/dbus/v5 v5.0.6
github.com/godbus/dbus/v5 v5.1.0
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
github.com/golangci/golangci-lint v1.52.2
github.com/google/go-cmp v0.5.9
github.com/google/go-containerregistry v0.9.0
github.com/google/go-containerregistry v0.14.0
github.com/google/nftables v0.1.1-0.20230115205135-9aa6fdf5a28c
github.com/google/uuid v1.3.0
github.com/goreleaser/nfpm v1.10.3
github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3
github.com/hdevalence/ed25519consensus v0.1.0
github.com/iancoleman/strcase v0.2.0
github.com/illarion/gonotify v1.0.1
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86
github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b
github.com/jsimonetti/rtnetlink v1.3.2
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/klauspost/compress v1.15.4
github.com/klauspost/compress v1.16.5
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a
github.com/mattn/go-colorable v0.1.12
github.com/mattn/go-isatty v0.0.14
github.com/mdlayher/genetlink v1.2.0
github.com/mdlayher/netlink v1.7.1
github.com/mattn/go-colorable v0.1.13
github.com/mattn/go-isatty v0.0.18
github.com/mdlayher/genetlink v1.3.2
github.com/mdlayher/netlink v1.7.2
github.com/mdlayher/sdnotify v1.0.0
github.com/miekg/dns v1.1.43
github.com/miekg/dns v1.1.54
github.com/mitchellh/go-ps v1.0.0
github.com/peterbourgon/ff/v3 v3.1.2
github.com/peterbourgon/ff/v3 v3.3.0
github.com/pkg/errors v0.9.1
github.com/pkg/sftp v1.13.4
github.com/prometheus/client_golang v1.14.0
github.com/prometheus/common v0.41.0
github.com/pkg/sftp v1.13.5
github.com/prometheus/client_golang v1.15.1
github.com/prometheus/common v0.42.0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502
github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41
github.com/tailscale/golang-x-crypto v0.0.0-20221102133106-bc99ab8c2d17
github.com/tailscale/golang-x-crypto v0.0.0-20221115211329-17a3db2c30d2
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05
github.com/tailscale/hujson v0.0.0-20220630195928-54599719472f
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a
github.com/tailscale/mkctr v0.0.0-20220601142259-c0b937af2e89
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85
github.com/tailscale/wireguard-go v0.0.0-20230410165232-af172621b4dd
github.com/tc-hib/winres v0.1.6
github.com/tc-hib/winres v0.2.0
github.com/tcnksm/go-httpstat v0.2.0
github.com/toqueteos/webbrowser v1.2.0
github.com/u-root/u-root v0.9.1-0.20230109201855-948a78c969ad
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54
go.uber.org/zap v1.21.0
go4.org/mem v0.0.0-20210711025021-927187094b94
go4.org/netipx v0.0.0-20220725152314-7e7bdc8411bf
golang.org/x/crypto v0.6.0
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db
golang.org/x/mod v0.7.0
golang.org/x/net v0.7.0
golang.org/x/oauth2 v0.5.0
github.com/u-root/u-root v0.11.0
github.com/vishvananda/netlink v1.2.1-beta.2
go.uber.org/zap v1.24.0
go4.org/mem v0.0.0-20220726221520-4f986261bf13
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35
golang.org/x/crypto v0.8.0
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53
golang.org/x/mod v0.10.0
golang.org/x/net v0.9.0
golang.org/x/oauth2 v0.7.0
golang.org/x/sync v0.1.0
golang.org/x/sys v0.5.1-0.20230222185716-a3b23cc77e89
golang.org/x/term v0.5.0
golang.org/x/time v0.0.0-20220609170525-579cf78fd858
golang.org/x/tools v0.5.0
golang.org/x/sys v0.7.0
golang.org/x/term v0.7.0
golang.org/x/time v0.3.0
golang.org/x/tools v0.8.0
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
golang.zx2c4.com/wireguard/windows v0.5.3
gvisor.dev/gvisor v0.0.0-20230328175328-162ed5ef888d
honnef.co/go/tools v0.4.2
honnef.co/go/tools v0.4.3
inet.af/peercred v0.0.0-20210906144145-0893ea02156a
inet.af/tcpproxy v0.0.0-20221017015627-91f861402626
inet.af/wf v0.0.0-20220728202103-50d96caab2f6
inet.af/wf v0.0.0-20221017222439-36129f591884
k8s.io/api v0.25.0
k8s.io/apimachinery v0.25.0
k8s.io/client-go v0.25.0
@@ -99,103 +100,109 @@ require (
)
require (
4d63.com/gochecknoglobals v0.1.0 // indirect
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
github.com/Antonboom/errname v0.1.5 // indirect
github.com/Antonboom/nilnil v0.1.0 // indirect
4d63.com/gocheckcompilerdirectives v1.2.1 // indirect
4d63.com/gochecknoglobals v0.2.1 // indirect
filippo.io/edwards25519 v1.0.0 // indirect
github.com/Abirdcfly/dupword v0.0.11 // indirect
github.com/Antonboom/errname v0.1.9 // indirect
github.com/Antonboom/nilnil v0.1.4 // indirect
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/Djarvur/go-err113 v0.1.0 // indirect
github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/OpenPeeDeeP/depguard v1.0.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/OpenPeeDeeP/depguard v1.1.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230426101702-58e86b294756 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/alexkohler/prealloc v1.0.0 // indirect
github.com/ashanbrown/forbidigo v1.2.0 // indirect
github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.0.0 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.6.4 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.8.2 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.5.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.5.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.9.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.6.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.11.1 // indirect
github.com/alingse/asasalint v0.0.11 // indirect
github.com/ashanbrown/forbidigo v1.5.1 // indirect
github.com/ashanbrown/makezero v1.1.1 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.13.21 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.25 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.28 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.12.9 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.18.10 // indirect
github.com/aws/smithy-go v1.13.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bkielbasa/cyclop v1.2.0 // indirect
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect
github.com/blizzy78/varnamelen v0.5.0 // indirect
github.com/bombsimon/wsl/v3 v3.3.0 // indirect
github.com/breml/bidichk v0.2.1 // indirect
github.com/butuzov/ireturn v0.1.1 // indirect
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e // indirect
github.com/blizzy78/varnamelen v0.8.0 // indirect
github.com/bombsimon/wsl/v3 v3.4.0 // indirect
github.com/breml/bidichk v0.2.4 // indirect
github.com/breml/errchkjson v0.3.1 // indirect
github.com/butuzov/ireturn v0.2.0 // indirect
github.com/cavaliergopher/cpio v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/charithe/durationcheck v0.0.9 // indirect
github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.11.4 // indirect
github.com/daixiang0/gci v0.2.9 // indirect
github.com/charithe/durationcheck v0.0.10 // indirect
github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/curioswitch/go-reassign v0.2.0 // indirect
github.com/daixiang0/gci v0.10.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/denis-tingajkin/go-header v0.4.2 // indirect
github.com/docker/cli v20.10.16+incompatible // indirect
github.com/denis-tingaikin/go-header v0.4.3 // indirect
github.com/docker/cli v23.0.5+incompatible // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker v20.10.16+incompatible // indirect
github.com/docker/docker-credential-helpers v0.6.4 // indirect
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
github.com/esimonov/ifshort v1.0.3 // indirect
github.com/docker/docker v23.0.5+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/emicklei/go-restful/v3 v3.10.2 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/esimonov/ifshort v1.0.4 // indirect
github.com/ettle/strcase v0.1.1 // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/fatih/structtag v1.2.0 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/fzipp/gocyclo v0.3.1 // indirect
github.com/gliderlabs/ssh v0.3.3 // indirect
github.com/go-critic/go-critic v0.6.1 // indirect
github.com/firefart/nonamedreturns v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/fzipp/gocyclo v0.6.0 // indirect
github.com/go-critic/go-critic v0.8.0 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.3.1 // indirect
github.com/go-git/go-git/v5 v5.4.2 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.5 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/go-toolsmith/astcast v1.0.0 // indirect
github.com/go-toolsmith/astcopy v1.0.0 // indirect
github.com/go-toolsmith/astequal v1.0.1 // indirect
github.com/go-toolsmith/astfmt v1.0.0 // indirect
github.com/go-toolsmith/astp v1.0.0 // indirect
github.com/go-toolsmith/strparse v1.0.0 // indirect
github.com/go-toolsmith/typep v1.0.2 // indirect
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850 // indirect
github.com/go-git/go-billy/v5 v5.4.1 // indirect
github.com/go-git/go-git/v5 v5.6.1 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-toolsmith/astcast v1.1.0 // indirect
github.com/go-toolsmith/astcopy v1.1.0 // indirect
github.com/go-toolsmith/astequal v1.1.0 // indirect
github.com/go-toolsmith/astfmt v1.1.0 // indirect
github.com/go-toolsmith/astp v1.1.0 // indirect
github.com/go-toolsmith/strparse v1.1.0 // indirect
github.com/go-toolsmith/typep v1.1.0 // indirect
github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 // indirect
github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a // indirect
github.com/golangci/golangci-lint v1.43.0 // indirect
github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe // indirect
github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 // indirect
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 // indirect
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca // indirect
github.com/golangci/misspell v0.3.5 // indirect
github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2 // indirect
github.com/golangci/misspell v0.4.0 // indirect
github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 // indirect
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic v0.6.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/goterm v0.0.0-20200907032337-555d40f16ae2 // indirect
github.com/google/rpmpack v0.0.0-20201206194719-59e495f2b7e1 // indirect
github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 // indirect
github.com/google/rpmpack v0.0.0-20221120200012-98b63d62fd77 // indirect
github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28 // indirect
github.com/goreleaser/chglog v0.1.2 // indirect
github.com/goreleaser/fileglob v0.3.1 // indirect
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
@@ -204,10 +211,12 @@ require (
github.com/gostaticanalysis/nilerr v0.1.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jgautheron/goconst v1.5.1 // indirect
github.com/jingyugao/rowserrcheck v1.1.1 // indirect
@@ -215,116 +224,133 @@ require (
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/julz/importas v0.0.0-20210922140945-27e0a5d4dee2 // indirect
github.com/kevinburke/ssh_config v1.1.0 // indirect
github.com/kisielk/errcheck v1.6.0 // indirect
github.com/julz/importas v0.1.0 // indirect
github.com/junk1tm/musttag v0.5.0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/kisielk/errcheck v1.6.3 // indirect
github.com/kisielk/gotool v1.0.0 // indirect
github.com/kkHAIKE/contextcheck v1.1.4 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/kulti/thelper v0.4.0 // indirect
github.com/kunwardeep/paralleltest v1.0.3 // indirect
github.com/kyoh86/exportloopref v0.1.8 // indirect
github.com/ldez/gomoddirectives v0.2.2 // indirect
github.com/ldez/tagliatelle v0.2.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/maratori/testpackage v1.0.1 // indirect
github.com/matoous/godox v0.0.0-20210227103229-6504466cf951 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/kulti/thelper v0.6.3 // indirect
github.com/kunwardeep/paralleltest v1.0.6 // indirect
github.com/kyoh86/exportloopref v0.1.11 // indirect
github.com/ldez/gomoddirectives v0.2.3 // indirect
github.com/ldez/tagliatelle v0.5.0 // indirect
github.com/leonklingele/grouper v1.1.1 // indirect
github.com/lufeee/execinquery v1.2.1 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/maratori/testableexamples v1.0.0 // indirect
github.com/maratori/testpackage v1.1.1 // indirect
github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mbilski/exhaustivestruct v1.2.0 // indirect
github.com/mdlayher/socket v0.4.0 // indirect
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 // indirect
github.com/mgechev/revive v1.1.2 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/mgechev/revive v1.3.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/moricho/tparallel v0.2.1 // indirect
github.com/moricho/tparallel v0.3.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nakabonne/nestif v0.3.1 // indirect
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/nishanths/exhaustive v0.7.11 // indirect
github.com/nishanths/predeclared v0.2.1 // indirect
github.com/nishanths/exhaustive v0.10.0 // indirect
github.com/nishanths/predeclared v0.2.2 // indirect
github.com/nunnatsa/ginkgolinter v0.11.2 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/gomega v1.20.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d // indirect
github.com/opencontainers/image-spec v1.1.0-rc3 // indirect
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/polyfloyd/go-errorlint v0.0.0-20211125173453-6d6d39c5bb8b // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/polyfloyd/go-errorlint v1.4.1 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/quasilyte/go-ruleguard v0.3.13 // indirect
github.com/quasilyte/go-ruleguard v0.3.19 // indirect
github.com/quasilyte/gogrep v0.5.0 // indirect
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/ryancurrah/gomodguard v1.2.3 // indirect
github.com/ryanrolds/sqlclosecheck v0.3.0 // indirect
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/ryancurrah/gomodguard v1.3.0 // indirect
github.com/ryanrolds/sqlclosecheck v0.4.0 // indirect
github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect
github.com/sassoftware/go-rpmutils v0.1.0 // indirect
github.com/securego/gosec/v2 v2.9.3 // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
github.com/sashamelentyev/usestdlibvars v1.23.0 // indirect
github.com/sassoftware/go-rpmutils v0.2.0 // indirect
github.com/securego/gosec/v2 v2.15.0 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/sivchari/tenv v1.4.7 // indirect
github.com/sonatard/noctx v0.0.1 // indirect
github.com/sourcegraph/go-diff v0.6.1 // indirect
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/cobra v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/sivchari/containedctx v1.0.3 // indirect
github.com/sivchari/nosnakecase v1.7.0 // indirect
github.com/sivchari/tenv v1.7.1 // indirect
github.com/skeema/knownhosts v1.1.0 // indirect
github.com/sonatard/noctx v0.0.2 // indirect
github.com/sourcegraph/go-diff v0.7.0 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.9.0 // indirect
github.com/spf13/viper v1.15.0 // indirect
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
github.com/stretchr/objx v0.4.0 // indirect
github.com/stretchr/testify v1.8.0 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/sylvia7788/contextcheck v1.0.4 // indirect
github.com/tdakkota/asciicheck v0.1.1 // indirect
github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/stretchr/testify v1.8.2 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect
github.com/tdakkota/asciicheck v0.2.0 // indirect
github.com/tetafro/godot v1.4.11 // indirect
github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144 // indirect
github.com/tomarrell/wrapcheck/v2 v2.4.0 // indirect
github.com/tommy-muehle/go-mnd/v2 v2.4.0 // indirect
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect
github.com/timonwong/loggercheck v0.9.4 // indirect
github.com/tomarrell/wrapcheck/v2 v2.8.1 // indirect
github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/ultraware/funlen v0.0.3 // indirect
github.com/ultraware/whitespace v0.0.4 // indirect
github.com/uudashr/gocognit v1.0.5 // indirect
github.com/ultraware/whitespace v0.0.5 // indirect
github.com/uudashr/gocognit v1.0.6 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/ssh-agent v0.3.1 // indirect
github.com/yeya24/promlinter v0.1.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a // indirect
golang.org/x/image v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/yagipy/maintidx v1.0.0 // indirect
github.com/yeya24/promlinter v0.2.0 // indirect
gitlab.com/bosi/decorder v0.2.3 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp/typeparams v0.0.0-20230425010034-47ecfdc1ba53 // indirect
golang.org/x/image v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
howett.net/plist v1.0.0 // indirect
k8s.io/apiextensions-apiserver v0.25.0 // indirect
k8s.io/component-base v0.25.0 // indirect
k8s.io/klog/v2 v2.70.1 // indirect
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
mvdan.cc/gofumpt v0.2.0 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
mvdan.cc/gofumpt v0.5.0 // indirect
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect
mvdan.cc/unparam v0.0.0-20211002134041-24922b6997ca // indirect
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
mvdan.cc/unparam v0.0.0-20230312165513-e84e2d14e3b8 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
)

View File

@@ -1 +1 @@
sha256-LfHkNXQbg7/u6y882gtINuQtwinYakg3abKJTDrrADo=
sha256-ZQ6aE+9PfAxfeNQeDzwcOCXpztLORVriHkEw51lbeHM=

1286
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -405,7 +405,7 @@ func DisabledEtcAptSource() bool {
return false
}
mod := fi.ModTime()
if c, ok := etcAptSrcCache.Load().(etcAptSrcResult); ok && c.mod == mod {
if c, ok := etcAptSrcCache.Load().(etcAptSrcResult); ok && c.mod.Equal(mod) {
return c.disabled
}
f, err := os.Open(path)

View File

@@ -6,6 +6,7 @@
package tooldeps
import (
_ "github.com/golangci/golangci-lint/cmd/golangci-lint"
_ "github.com/tailscale/depaware/depaware"
_ "golang.org/x/tools/cmd/goimports"
)

View File

@@ -17,6 +17,7 @@ import (
"time"
"tailscale.com/envknob"
"tailscale.com/net/sockstats"
"tailscale.com/tailcfg"
"tailscale.com/util/clientmetric"
"tailscale.com/util/goroutines"
@@ -94,7 +95,8 @@ func (b *LocalBackend) handleC2N(w http.ResponseWriter, r *http.Request) {
return
}
b.sockstatLogger.Flush()
fmt.Fprintln(w, b.sockstatLogger.LogID())
fmt.Fprintf(w, "logid: %s\n", b.sockstatLogger.LogID())
fmt.Fprintf(w, "debug info: %v\n", sockstats.DebugInfo())
default:
http.Error(w, "unknown c2n path", http.StatusBadRequest)
}

View File

@@ -158,7 +158,7 @@ func (em *expiryManager) nextPeerExpiry(nm *netmap.NetworkMap, localNow time.Tim
// nextExpiry being zero is a sentinel that we haven't yet set
// an expiry; otherwise, only update if this node's expiry is
// sooner than the currently-stored one (since we want the
// soonest-occuring expiry time).
// soonest-occurring expiry time).
if nextExpiry.IsZero() || peer.KeyExpiry.Before(nextExpiry) {
nextExpiry = peer.KeyExpiry
}

View File

@@ -243,7 +243,7 @@ func TestNextPeerExpiry(t *testing.T) {
em := newExpiryManager(t.Logf)
em.timeNow = func() time.Time { return now }
got := em.nextPeerExpiry(tt.netmap, now)
if got != tt.want {
if !got.Equal(tt.want) {
t.Errorf("got %q, want %q", got.Format(time.RFC3339), tt.want.Format(time.RFC3339))
} else if !got.IsZero() && got.Before(now) {
t.Errorf("unexpectedly got expiry %q before now %q", got.Format(time.RFC3339), now.Format(time.RFC3339))
@@ -269,7 +269,7 @@ func TestNextPeerExpiry(t *testing.T) {
}
got := em.nextPeerExpiry(nm, now)
want := now.Add(30 * time.Second)
if got != want {
if !got.Equal(want) {
t.Errorf("got %q, want %q", got.Format(time.RFC3339), want.Format(time.RFC3339))
}
})

View File

@@ -142,7 +142,7 @@ type LocalBackend struct {
store ipn.StateStore
dialer *tsdial.Dialer // non-nil
backendLogID logid.PublicID
unregisterLinkMon func()
unregisterNetMon func()
unregisterHealthWatch func()
portpoll *portlist.Poller // may be nil
portpollOnce sync.Once // guards starting readPoller
@@ -313,7 +313,7 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, store ipn.StateStor
loginFlags: loginFlags,
}
b.sockstatLogger, err = sockstatlog.NewLogger(logpolicy.LogsDir(logf), logf, logID)
b.sockstatLogger, err = sockstatlog.NewLogger(logpolicy.LogsDir(logf), logf, logID, e.GetNetMon())
if err != nil {
log.Printf("error setting up sockstat logger: %v", err)
}
@@ -330,12 +330,12 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, store ipn.StateStor
b.statusChanged = sync.NewCond(&b.statusLock)
b.e.SetStatusCallback(b.setWgengineStatus)
linkMon := e.GetLinkMonitor()
b.prevIfState = linkMon.InterfaceState()
netMon := e.GetNetMon()
b.prevIfState = netMon.InterfaceState()
// Call our linkChange code once with the current state, and
// then also whenever it changes:
b.linkChange(false, linkMon.InterfaceState())
b.unregisterLinkMon = linkMon.RegisterChangeCallback(b.linkChange)
b.linkChange(false, netMon.InterfaceState())
b.unregisterNetMon = netMon.RegisterChangeCallback(b.linkChange)
b.unregisterHealthWatch = health.RegisterWatcher(b.onHealthChange)
@@ -438,7 +438,7 @@ func (b *LocalBackend) SetComponentDebugLogging(component string, until time.Tim
// unchanged when the timer actually fires.
b.mu.Lock()
defer b.mu.Unlock()
if ls := b.componentLogUntil[component]; ls.until == until {
if ls := b.componentLogUntil[component]; ls.until.Equal(until) {
setEnabled(false)
b.logf("debugging logging for component %q disabled (by timer)", component)
}
@@ -499,7 +499,7 @@ func (b *LocalBackend) maybePauseControlClientLocked() {
b.cc.SetPaused((b.state == ipn.Stopped && b.netMap != nil) || !networkUp)
}
// linkChange is our link monitor callback, called whenever the network changes.
// linkChange is our network monitor callback, called whenever the network changes.
// major is whether ifst is different than earlier.
func (b *LocalBackend) linkChange(major bool, ifst *interfaces.State) {
b.mu.Lock()
@@ -576,7 +576,7 @@ func (b *LocalBackend) Shutdown() {
b.sockstatLogger.Shutdown()
}
b.unregisterLinkMon()
b.unregisterNetMon()
b.unregisterHealthWatch()
if cc != nil {
cc.Shutdown()
@@ -1077,7 +1077,7 @@ func (b *LocalBackend) setClientStatus(st controlclient.Status) {
b.e.SetDERPMap(st.NetMap.DERPMap)
// Update our cached DERP map
dnsfallback.UpdateCache(st.NetMap.DERPMap)
dnsfallback.UpdateCache(st.NetMap.DERPMap, b.logf)
b.send(ipn.Notify{NetMap: st.NetMap})
}
@@ -1423,7 +1423,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
HTTPTestClient: httpTestClient,
DiscoPublicKey: discoPublic,
DebugFlags: debugFlags,
LinkMonitor: b.e.GetLinkMonitor(),
NetMon: b.e.GetNetMon(),
Pinger: b,
PopBrowserURL: b.tellClientToBrowseToURL,
OnClientVersion: b.onClientVersion,
@@ -2479,7 +2479,7 @@ func (b *LocalBackend) parseWgStatusLocked(s *wgengine.Status) (ret ipn.EngineSt
// [GRINDER STATS LINES] - please don't remove (used for log parsing)
if peerStats.Len() > 0 {
b.keyLogf("[v1] peer keys: %s", strings.TrimSpace(peerKeys.String()))
b.statsLogf("[v1] v%v peers: %v", version.Long, strings.TrimSpace(peerStats.String()))
b.statsLogf("[v1] v%v peers: %v", version.Long(), strings.TrimSpace(peerStats.String()))
}
return ret
}

View File

@@ -875,7 +875,7 @@ func TestTKAForceDisable(t *testing.T) {
}
if b.tka != nil {
t.Fatal("tka was re-initalized")
t.Fatal("tka was re-initialized")
}
}

View File

@@ -605,6 +605,16 @@ func (h *peerAPIHandler) logf(format string, a ...any) {
h.ps.b.logf("peerapi: "+format, a...)
}
// isAddressValid reports whether addr is a valid destination address for this
// node originating from the peer.
func (h *peerAPIHandler) isAddressValid(addr netip.Addr) bool {
if h.peerNode.SelfNodeV4MasqAddrForThisPeer != nil {
return *h.peerNode.SelfNodeV4MasqAddrForThisPeer == addr
}
pfx := netip.PrefixFrom(addr, addr.BitLen())
return slices.Contains(h.selfNode.Addresses, pfx)
}
func (h *peerAPIHandler) validateHost(r *http.Request) error {
if r.Host == "peer" {
return nil
@@ -613,9 +623,8 @@ func (h *peerAPIHandler) validateHost(r *http.Request) error {
if err != nil {
return err
}
hostIPPfx := netip.PrefixFrom(ap.Addr(), ap.Addr().BitLen())
if !slices.Contains(h.selfNode.Addresses, hostIPPfx) {
return fmt.Errorf("%v not found in self addresses", hostIPPfx)
if !h.isAddressValid(ap.Addr()) {
return fmt.Errorf("%v not found in self addresses", ap.Addr())
}
return nil
}
@@ -947,6 +956,12 @@ func (h *peerAPIHandler) handleServeSockStats(w http.ResponseWriter, r *http.Req
fmt.Fprintln(w, "</tfoot>")
fmt.Fprintln(w, "</table>")
fmt.Fprintln(w, "<h2>Debug Info</h2>")
fmt.Fprintln(w, "<pre>")
fmt.Fprintln(w, html.EscapeString(sockstats.DebugInfo()))
fmt.Fprintln(w, "</pre>")
}
type incomingFile struct {

View File

@@ -22,6 +22,8 @@ import (
"tailscale.com/util/winutil"
)
var errAlreadyMigrated = errors.New("profile migration already completed")
// profileManager is a wrapper around a StateStore that manages
// multiple profiles and the current profile.
type profileManager struct {
@@ -66,7 +68,7 @@ func (pm *profileManager) SetCurrentUserID(uid ipn.WindowsUserID) error {
b, err := pm.store.ReadState(ipn.CurrentProfileKey(string(uid)))
if err == ipn.ErrStateNotExist || len(b) == 0 {
if runtime.GOOS == "windows" {
if err := pm.migrateFromLegacyPrefs(); err != nil {
if err := pm.migrateFromLegacyPrefs(); err != nil && !errors.Is(err, errAlreadyMigrated) {
return err
}
} else {
@@ -544,7 +546,14 @@ func newProfileManagerWithGOOS(store ipn.StateStore, logf logger.Logf, goos stri
if err := pm.setPrefsLocked(prefs); err != nil {
return nil, err
}
} else if len(knownProfiles) == 0 && goos != "windows" {
// Most platform behavior is controlled by the goos parameter, however
// some behavior is implied by build tag and fails when run on Windows,
// so we explicitly avoid that behavior when running on Windows.
// Specifically this reaches down into legacy preference loading that is
// specialized by profiles_windows.go and fails in tests on an invalid
// uid passed in from the unix tests. The uid's used for Windows tests
// and runtime must be valid Windows security identifier structures.
} else if len(knownProfiles) == 0 && goos != "windows" && runtime.GOOS != "windows" {
// No known profiles, try a migration.
if err := pm.migrateFromLegacyPrefs(); err != nil {
return nil, err
@@ -562,7 +571,7 @@ func (pm *profileManager) migrateFromLegacyPrefs() error {
sentinel, prefs, err := pm.loadLegacyPrefs()
if err != nil {
metricMigrationError.Add(1)
return err
return fmt.Errorf("load legacy prefs: %w", err)
}
if err := pm.SetPrefs(prefs); err != nil {
metricMigrationError.Add(1)

View File

@@ -5,7 +5,7 @@ package ipnlocal
import (
"fmt"
"runtime"
"os/user"
"strconv"
"testing"
@@ -18,9 +18,6 @@ import (
)
func TestProfileCurrentUserSwitch(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("TODO(#7876): test regressed on windows while CI was broken")
}
store := new(mem.Store)
pm, err := newProfileManagerWithGOOS(store, logger.Discard, "linux")
@@ -77,9 +74,6 @@ func TestProfileCurrentUserSwitch(t *testing.T) {
}
func TestProfileList(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("TODO(#7876): test regressed on windows while CI was broken")
}
store := new(mem.Store)
pm, err := newProfileManagerWithGOOS(store, logger.Discard, "linux")
@@ -158,9 +152,6 @@ func TestProfileList(t *testing.T) {
// TestProfileManagement tests creating, loading, and switching profiles.
func TestProfileManagement(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("TODO(#7876): test regressed on windows while CI was broken")
}
store := new(mem.Store)
pm, err := newProfileManagerWithGOOS(store, logger.Discard, "linux")
@@ -312,10 +303,11 @@ func TestProfileManagement(t *testing.T) {
// TestProfileManagementWindows tests going into and out of Unattended mode on
// Windows.
func TestProfileManagementWindows(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("TODO(#7876): test regressed on windows while CI was broken")
u, err := user.Current()
if err != nil {
t.Fatal(err)
}
uid := ipn.WindowsUserID(u.Uid)
store := new(mem.Store)
@@ -365,8 +357,8 @@ func TestProfileManagementWindows(t *testing.T) {
{
t.Logf("Set user1 as logged in user")
if err := pm.SetCurrentUserID("user1"); err != nil {
t.Fatal(err)
if err := pm.SetCurrentUserID(uid); err != nil {
t.Fatalf("can't set user id: %s", err)
}
checkProfiles(t)
t.Logf("Save prefs for user1")
@@ -401,7 +393,7 @@ func TestProfileManagementWindows(t *testing.T) {
{
t.Logf("Set user1 as current user")
if err := pm.SetCurrentUserID("user1"); err != nil {
if err := pm.SetCurrentUserID(uid); err != nil {
t.Fatal(err)
}
wantCurProfile = "test"
@@ -411,8 +403,8 @@ func TestProfileManagementWindows(t *testing.T) {
t.Logf("set unattended mode")
wantProfiles["test"] = setPrefs(t, "test", true)
}
if pm.CurrentUserID() != "user1" {
t.Fatalf("CurrentUserID = %q; want %q", pm.CurrentUserID(), "user1")
if pm.CurrentUserID() != uid {
t.Fatalf("CurrentUserID = %q; want %q", pm.CurrentUserID(), uid)
}
// Recreate the profile manager to ensure that it starts with test profile.
@@ -421,7 +413,7 @@ func TestProfileManagementWindows(t *testing.T) {
t.Fatal(err)
}
checkProfiles(t)
if pm.CurrentUserID() != "user1" {
t.Fatalf("CurrentUserID = %q; want %q", pm.CurrentUserID(), "user1")
if pm.CurrentUserID() != uid {
t.Fatalf("CurrentUserID = %q; want %q", pm.CurrentUserID(), uid)
}
}

View File

@@ -6,6 +6,7 @@ package ipnlocal
import (
"errors"
"fmt"
"io/fs"
"os"
"os/user"
"path/filepath"
@@ -21,8 +22,6 @@ const (
legacyPrefsExt = ".conf"
)
var errAlreadyMigrated = errors.New("profile migration already completed")
func legacyPrefsDir(uid ipn.WindowsUserID) (string, error) {
// TODO(aaron): Ideally we'd have the impersonation token for the pipe's
// client and use it to call SHGetKnownFolderPath, thus yielding the correct
@@ -56,6 +55,9 @@ func (pm *profileManager) loadLegacyPrefs() (string, ipn.PrefsView, error) {
prefsPath := filepath.Join(userLegacyPrefsDir, legacyPrefsFile+legacyPrefsExt)
prefs, err := ipn.LoadPrefs(prefsPath)
if errors.Is(err, fs.ErrNotExist) {
return "", ipn.PrefsView{}, errAlreadyMigrated
}
if err != nil {
return "", ipn.PrefsView{}, err
}

View File

@@ -17,7 +17,6 @@ import (
"net/url"
"os"
"path"
pathpkg "path"
"strconv"
"strings"
"sync"
@@ -144,7 +143,7 @@ func (s *serveListener) Run() {
}
func (s *serveListener) shouldWarnAboutListenError(err error) bool {
if !s.b.e.GetLinkMonitor().InterfaceState().HasIP(s.ap.Addr()) {
if !s.b.e.GetNetMon().InterfaceState().HasIP(s.ap.Addr()) {
// Machine likely doesn't have IPv6 enabled (or the IP is still being
// assigned). No need to warn. Notably, WSL2 (Issue 6303).
return false
@@ -420,19 +419,19 @@ func (b *LocalBackend) getServeHandler(r *http.Request) (_ ipn.HTTPHandlerView,
if h, ok := wsc.Handlers().GetOk(r.URL.Path); ok {
return h, r.URL.Path, true
}
path := path.Clean(r.URL.Path)
pth := path.Clean(r.URL.Path)
for {
withSlash := path + "/"
withSlash := pth + "/"
if h, ok := wsc.Handlers().GetOk(withSlash); ok {
return h, withSlash, true
}
if h, ok := wsc.Handlers().GetOk(path); ok {
return h, path, true
if h, ok := wsc.Handlers().GetOk(pth); ok {
return h, pth, true
}
if path == "/" {
if pth == "/" {
return z, "", false
}
path = pathpkg.Dir(path)
pth = path.Dir(pth)
}
}

View File

@@ -37,7 +37,8 @@ func (s *Server) handleProxyConnectConn(w http.ResponseWriter, r *http.Request)
return
}
back, err := logpolicy.DialContext(ctx, "tcp", hostPort)
dialContext := logpolicy.MakeDialFunc(s.netMon)
back, err := dialContext(ctx, "tcp", hostPort)
if err != nil {
s.logf("error CONNECT dialing %v: %v", hostPort, err)
http.Error(w, "Connect failure", http.StatusBadGateway)

View File

@@ -24,6 +24,7 @@ import (
"tailscale.com/ipn/ipnauth"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/ipn/localapi"
"tailscale.com/net/netmon"
"tailscale.com/types/logger"
"tailscale.com/types/logid"
"tailscale.com/util/mak"
@@ -36,6 +37,7 @@ import (
type Server struct {
lb atomic.Pointer[ipnlocal.LocalBackend]
logf logger.Logf
netMon *netmon.Monitor // optional; nil means interfaces will be looked up on-demand
backendLogID logid.PublicID
// resetOnZero is whether to call bs.Reset on transition from
// 1->0 active HTTP requests. That is, this is whether the backend is
@@ -197,7 +199,7 @@ func (s *Server) serveHTTP(w http.ResponseWriter, r *http.Request) {
defer onDone()
if strings.HasPrefix(r.URL.Path, "/localapi/") {
lah := localapi.NewHandler(lb, s.logf, s.backendLogID)
lah := localapi.NewHandler(lb, s.logf, s.netMon, s.backendLogID)
lah.PermitRead, lah.PermitWrite = s.localAPIPermissions(ci)
lah.PermitCert = s.connCanFetchCerts(ci)
lah.ServeHTTP(w, r)
@@ -408,15 +410,18 @@ func (s *Server) addActiveHTTPRequest(req *http.Request, ci *ipnauth.ConnIdentit
}
// New returns a new Server.
// The netMon parameter is optional; if non-nil it's used to do faster interface
// lookups.
//
// To start it, use the Server.Run method.
//
// At some point, either before or after Run, the Server's SetLocalBackend
// method must also be called before Server can do anything useful.
func New(logf logger.Logf, logID logid.PublicID) *Server {
func New(logf logger.Logf, logID logid.PublicID, netMon *netmon.Monitor) *Server {
return &Server{
backendLogID: logID,
logf: logf,
netMon: netMon,
resetOnZero: envknob.GOOS() == "windows",
}
}

View File

@@ -4,17 +4,24 @@
package localapi
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"net"
"net/http"
"net/netip"
"strconv"
"time"
"tailscale.com/derp/derphttp"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/netaddr"
"tailscale.com/net/netns"
"tailscale.com/net/stun"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/nettype"
)
func (h *Handler) serveDebugDERPRegion(w http.ResponseWriter, r *http.Request) {
@@ -132,6 +139,92 @@ func (h *Handler) serveDebugDERPRegion(w http.ResponseWriter, r *http.Request) {
return hasIPv4 || hasIPv6
}
checkSTUN4 := func(derpNode *tailcfg.DERPNode) {
u4, err := nettype.MakePacketListenerWithNetIP(netns.Listener(h.logf, h.netMon)).ListenPacket(ctx, "udp4", ":0")
if err != nil {
st.Errors = append(st.Errors, fmt.Sprintf("Error creating IPv4 STUN listener: %v", err))
return
}
defer u4.Close()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var addr netip.Addr
if derpNode.IPv4 != "" {
addr, err = netip.ParseAddr(derpNode.IPv4)
if err != nil {
// Error printed elsewhere
return
}
} else {
addrs, err := net.DefaultResolver.LookupNetIP(ctx, "ip4", derpNode.HostName)
if err != nil {
st.Errors = append(st.Errors, fmt.Sprintf("Error resolving node %q IPv4 addresses: %v", derpNode.HostName, err))
return
}
addr = addrs[0]
}
addrPort := netip.AddrPortFrom(addr, uint16(firstNonzero(derpNode.STUNPort, 3478)))
txID := stun.NewTxID()
req := stun.Request(txID)
done := make(chan struct{})
defer close(done)
go func() {
select {
case <-ctx.Done():
case <-done:
}
u4.Close()
}()
gotResponse := make(chan netip.AddrPort, 1)
go func() {
defer u4.Close()
var buf [64 << 10]byte
for {
n, addr, err := u4.ReadFromUDPAddrPort(buf[:])
if err != nil {
return
}
pkt := buf[:n]
if !stun.Is(pkt) {
continue
}
ap := netaddr.Unmap(addr)
if !ap.IsValid() {
continue
}
tx, addrPort, err := stun.ParseResponse(pkt)
if err != nil {
continue
}
if tx == txID {
gotResponse <- addrPort
return
}
}
}()
_, err = u4.WriteToUDPAddrPort(req, addrPort)
if err != nil {
st.Errors = append(st.Errors, fmt.Sprintf("Error sending IPv4 STUN packet to %v (%q): %v", addrPort, derpNode.HostName, err))
return
}
select {
case resp := <-gotResponse:
st.Info = append(st.Info, fmt.Sprintf("Node %q returned IPv4 STUN response: %v", derpNode.HostName, resp))
case <-ctx.Done():
st.Warnings = append(st.Warnings, fmt.Sprintf("Node %q did not return a IPv4 STUN response", derpNode.HostName))
}
}
// Start by checking whether we can establish a HTTP connection
for _, derpNode := range reg.Nodes {
connSuccess := checkConn(derpNode)
@@ -156,7 +249,7 @@ func (h *Handler) serveDebugDERPRegion(w http.ResponseWriter, r *http.Request) {
serverPubKeys := make(map[key.NodePublic]bool)
for i := 0; i < 5; i++ {
func() {
rc := derphttp.NewRegionClient(fakePrivKey, h.logf, func() *tailcfg.DERPRegion {
rc := derphttp.NewRegionClient(fakePrivKey, h.logf, h.netMon, func() *tailcfg.DERPRegion {
return &tailcfg.DERPRegion{
RegionID: reg.RegionID,
RegionCode: reg.RegionCode,
@@ -178,6 +271,10 @@ func (h *Handler) serveDebugDERPRegion(w http.ResponseWriter, r *http.Request) {
if len(serverPubKeys) > 1 {
st.Errors = append(st.Errors, fmt.Sprintf("Received multiple server public keys (%d); is the DERP server behind a load balancer?", len(serverPubKeys)))
}
// Send a STUN query to this node to verify whether or not it
// correctly returns an IP address.
checkSTUN4(derpNode)
}
// TODO(bradfitz): finish:
@@ -191,7 +288,6 @@ func (h *Handler) serveDebugDERPRegion(w http.ResponseWriter, r *http.Request) {
// protocol to say how many peers it's meshed with. Should match count
// in DERPRegion. Or maybe even list all their server pub keys that it's peered
// with.
// * try STUN queries
// * If their certificate is bad, either expired or just wrongly
// issued in the first place, tell them specifically that the
// cert is bad not just that the connection failed.

View File

@@ -34,6 +34,7 @@ import (
"tailscale.com/ipn/ipnlocal"
"tailscale.com/ipn/ipnstate"
"tailscale.com/logtail"
"tailscale.com/net/netmon"
"tailscale.com/net/netutil"
"tailscale.com/net/portmapper"
"tailscale.com/tailcfg"
@@ -46,7 +47,6 @@ import (
"tailscale.com/util/httpm"
"tailscale.com/util/mak"
"tailscale.com/version"
"tailscale.com/wgengine/monitor"
)
type localAPIHandler func(*Handler, http.ResponseWriter, *http.Request)
@@ -125,8 +125,10 @@ var (
metrics = map[string]*clientmetric.Metric{}
)
func NewHandler(b *ipnlocal.LocalBackend, logf logger.Logf, logID logid.PublicID) *Handler {
return &Handler{b: b, logf: logf, backendLogID: logID}
// NewHandler creates a new LocalAPI HTTP handler. All parameters except netMon
// are required (if non-nil it's used to do faster interface lookups).
func NewHandler(b *ipnlocal.LocalBackend, logf logger.Logf, netMon *netmon.Monitor, logID logid.PublicID) *Handler {
return &Handler{b: b, logf: logf, netMon: netMon, backendLogID: logID}
}
type Handler struct {
@@ -150,6 +152,7 @@ type Handler struct {
b *ipnlocal.LocalBackend
logf logger.Logf
netMon *netmon.Monitor // optional; nil means interfaces will be looked up on-demand
backendLogID logid.PublicID
}
@@ -679,7 +682,7 @@ func (h *Handler) serveDebugPortmap(w http.ResponseWriter, r *http.Request) {
done := make(chan bool, 1)
var c *portmapper.Client
c = portmapper.NewClient(logger.WithPrefix(logf, "portmapper: "), debugKnobs, func() {
c = portmapper.NewClient(logger.WithPrefix(logf, "portmapper: "), h.netMon, debugKnobs, func() {
logf("portmapping changed.")
logf("have mapping: %v", c.HaveMapping())
@@ -695,7 +698,7 @@ func (h *Handler) serveDebugPortmap(w http.ResponseWriter, r *http.Request) {
})
defer c.Close()
linkMon, err := monitor.New(logger.WithPrefix(logf, "monitor: "))
netMon, err := netmon.New(logger.WithPrefix(logf, "monitor: "))
if err != nil {
logf("error creating monitor: %v", err)
return
@@ -707,14 +710,14 @@ func (h *Handler) serveDebugPortmap(w http.ResponseWriter, r *http.Request) {
self = netip.MustParseAddr(b)
return gw, self, true
}
return linkMon.GatewayAndSelfIP()
return netMon.GatewayAndSelfIP()
}
c.SetGatewayLookupFunc(gatewayAndSelfIP)
gw, selfIP, ok := gatewayAndSelfIP()
if !ok {
logf("no gateway or self IP; %v", linkMon.InterfaceState())
logf("no gateway or self IP; %v", netMon.InterfaceState())
return
}
logf("gw=%v; self=%v", gw, selfIP)

View File

@@ -51,6 +51,12 @@ type awsStore struct {
// New returns a new ipn.StateStore using the AWS SSM storage
// location given by ssmARN.
//
// Note that we store the entire store in a single parameter
// key, therefore if the state is above 8kb, it can cause
// Tailscaled to only only store new state in-memory and
// restarting Tailscaled can fail until you delete your state
// from the AWS Parameter Store.
func New(_ logger.Logf, ssmARN string) (ipn.StateStore, error) {
return newStore(ssmARN, nil)
}
@@ -160,14 +166,19 @@ func (s *awsStore) persistState() error {
return err
}
// Store in AWS SSM parameter store
// Store in AWS SSM parameter store.
//
// We use intelligent tiering so that when the state is below 4kb, it uses Standard tiering
// which is free. However, if it exceeds 4kb it switches the parameter to advanced tiering
// doubling the capacity to 8kb per the following docs:
// https://aws.amazon.com/about-aws/whats-new/2019/08/aws-systems-manager-parameter-store-announces-intelligent-tiering-to-enable-automatic-parameter-tier-selection/
_, err = s.ssmClient.PutParameter(
context.TODO(),
&ssm.PutParameterInput{
Name: aws.String(s.ParameterName()),
Value: aws.String(string(bs)),
Overwrite: aws.Bool(true),
Tier: ssmTypes.ParameterTierStandard,
Tier: ssmTypes.ParameterTierIntelligentTiering,
Type: ssmTypes.ParameterTypeSecureString,
},
)

View File

@@ -71,11 +71,11 @@ Client][]. See also the dependencies in the [Tailscale CLI][].
- [golang.org/x/exp](https://pkg.go.dev/golang.org/x/exp) ([BSD-3-Clause](https://cs.opensource.google/go/x/exp/+/47842c84:LICENSE))
- [golang.org/x/exp/shiny](https://pkg.go.dev/golang.org/x/exp/shiny) ([BSD-3-Clause](https://cs.opensource.google/go/x/exp/+/334a2380:shiny/LICENSE))
- [golang.org/x/image](https://pkg.go.dev/golang.org/x/image) ([BSD-3-Clause](https://cs.opensource.google/go/x/image/+/v0.5.0:LICENSE))
- [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.7.0:LICENSE))
- [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.8.0:LICENSE))
- [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) ([BSD-3-Clause](https://cs.opensource.google/go/x/sync/+/v0.1.0:LICENSE))
- [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/a3b23cc7:LICENSE))
- [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) ([BSD-3-Clause](https://cs.opensource.google/go/x/term/+/v0.5.0:LICENSE))
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.7.0:LICENSE))
- [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.6.0:LICENSE))
- [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) ([BSD-3-Clause](https://cs.opensource.google/go/x/term/+/v0.6.0:LICENSE))
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.8.0:LICENSE))
- [golang.org/x/time/rate](https://pkg.go.dev/golang.org/x/time/rate) ([BSD-3-Clause](https://cs.opensource.google/go/x/time/+/579cf78f:LICENSE))
- [gvisor.dev/gvisor/pkg](https://pkg.go.dev/gvisor.dev/gvisor/pkg) ([Apache-2.0](https://github.com/google/gvisor/blob/162ed5ef888d/LICENSE))
- [inet.af/netaddr](https://pkg.go.dev/inet.af/netaddr) ([BSD-3-Clause](https://github.com/inetaf/netaddr/blob/097006376321/LICENSE))

View File

@@ -59,11 +59,11 @@ and [iOS][]. See also the dependencies in the [Tailscale CLI][].
- [go4.org/netipx](https://pkg.go.dev/go4.org/netipx) ([BSD-3-Clause](https://github.com/go4org/netipx/blob/7e7bdc8411bf/LICENSE))
- [golang.org/x/crypto](https://pkg.go.dev/golang.org/x/crypto) ([BSD-3-Clause](https://cs.opensource.google/go/x/crypto/+/v0.6.0:LICENSE))
- [golang.org/x/exp](https://pkg.go.dev/golang.org/x/exp) ([BSD-3-Clause](https://cs.opensource.google/go/x/exp/+/cafedaf6:LICENSE))
- [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.7.0:LICENSE))
- [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.9.0:LICENSE))
- [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) ([BSD-3-Clause](https://cs.opensource.google/go/x/sync/+/v0.1.0:LICENSE))
- [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/a3b23cc7:LICENSE))
- [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) ([BSD-3-Clause](https://cs.opensource.google/go/x/term/+/v0.5.0:LICENSE))
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.7.0:LICENSE))
- [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.7.0:LICENSE))
- [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) ([BSD-3-Clause](https://cs.opensource.google/go/x/term/+/v0.7.0:LICENSE))
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.9.0:LICENSE))
- [golang.org/x/time/rate](https://pkg.go.dev/golang.org/x/time/rate) ([BSD-3-Clause](https://cs.opensource.google/go/x/time/+/579cf78f:LICENSE))
- [gvisor.dev/gvisor/pkg](https://pkg.go.dev/gvisor.dev/gvisor/pkg) ([Apache-2.0](https://github.com/google/gvisor/blob/162ed5ef888d/LICENSE))
- [inet.af/peercred](https://pkg.go.dev/inet.af/peercred) ([BSD-3-Clause](https://github.com/inetaf/peercred/blob/0893ea02156a/LICENSE))

View File

@@ -13,84 +13,87 @@ well as an [option for macOS][].
Some packages may only be included on certain architectures or operating systems.
- [filippo.io/edwards25519](https://pkg.go.dev/filippo.io/edwards25519) ([BSD-3-Clause](https://github.com/FiloSottile/edwards25519/blob/v1.0.0-rc.1/LICENSE))
- [github.com/Microsoft/go-winio](https://pkg.go.dev/github.com/Microsoft/go-winio) ([MIT](https://github.com/Microsoft/go-winio/blob/v0.6.0/LICENSE))
- [filippo.io/edwards25519](https://pkg.go.dev/filippo.io/edwards25519) ([BSD-3-Clause](https://github.com/FiloSottile/edwards25519/blob/v1.0.0/LICENSE))
- [github.com/Microsoft/go-winio](https://pkg.go.dev/github.com/Microsoft/go-winio) ([MIT](https://github.com/Microsoft/go-winio/blob/v0.6.1/LICENSE))
- [github.com/akutz/memconn](https://pkg.go.dev/github.com/akutz/memconn) ([Apache-2.0](https://github.com/akutz/memconn/blob/v0.1.0/LICENSE))
- [github.com/alexbrainman/sspi](https://pkg.go.dev/github.com/alexbrainman/sspi) ([BSD-3-Clause](https://github.com/alexbrainman/sspi/blob/909beea2cc74/LICENSE))
- [github.com/anmitsu/go-shlex](https://pkg.go.dev/github.com/anmitsu/go-shlex) ([MIT](https://github.com/anmitsu/go-shlex/blob/38f4b401e2be/LICENSE))
- [github.com/aws/aws-sdk-go-v2](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/v1.17.3/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/config](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/config) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/config/v1.11.0/config/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/credentials](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/credentials) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/credentials/v1.6.4/credentials/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/feature/ec2/imds](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/feature/ec2/imds) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/feature/ec2/imds/v1.8.2/feature/ec2/imds/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/internal/configsources](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/internal/configsources) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/internal/configsources/v1.1.27/internal/configsources/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/internal/endpoints/v2](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/internal/endpoints/v2.4.21/internal/endpoints/v2/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/internal/ini](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/internal/ini) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/internal/ini/v1.3.2/internal/ini/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/internal/sync/singleflight](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/internal/sync/singleflight) ([BSD-3-Clause](https://github.com/aws/aws-sdk-go-v2/blob/v1.17.3/internal/sync/singleflight/LICENSE))
- [github.com/aws/aws-sdk-go-v2/service/internal/presigned-url](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/service/internal/presigned-url/v1.5.2/service/internal/presigned-url/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/service/ssm](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/ssm) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/service/ssm/v1.35.0/service/ssm/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/service/sso](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/sso) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/service/sso/v1.6.2/service/sso/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/service/sts](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/sts) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/service/sts/v1.11.1/service/sts/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/v1.18.0/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/config](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/config) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/config/v1.18.22/config/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/credentials](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/credentials) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/credentials/v1.13.21/credentials/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/feature/ec2/imds](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/feature/ec2/imds) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/feature/ec2/imds/v1.13.3/feature/ec2/imds/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/internal/configsources](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/internal/configsources) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/internal/configsources/v1.1.33/internal/configsources/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/internal/endpoints/v2](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/internal/endpoints/v2.4.27/internal/endpoints/v2/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/internal/ini](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/internal/ini) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/internal/ini/v1.3.34/internal/ini/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/internal/sync/singleflight](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/internal/sync/singleflight) ([BSD-3-Clause](https://github.com/aws/aws-sdk-go-v2/blob/v1.18.0/internal/sync/singleflight/LICENSE))
- [github.com/aws/aws-sdk-go-v2/service/internal/presigned-url](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/service/internal/presigned-url/v1.9.27/service/internal/presigned-url/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/service/ssm](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/ssm) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/service/ssm/v1.36.3/service/ssm/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/service/sso](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/sso) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/service/sso/v1.12.9/service/sso/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/service/ssooidc](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/ssooidc) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/service/ssooidc/v1.14.9/service/ssooidc/LICENSE.txt))
- [github.com/aws/aws-sdk-go-v2/service/sts](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/sts) ([Apache-2.0](https://github.com/aws/aws-sdk-go-v2/blob/service/sts/v1.18.10/service/sts/LICENSE.txt))
- [github.com/aws/smithy-go](https://pkg.go.dev/github.com/aws/smithy-go) ([Apache-2.0](https://github.com/aws/smithy-go/blob/v1.13.5/LICENSE))
- [github.com/aws/smithy-go/internal/sync/singleflight](https://pkg.go.dev/github.com/aws/smithy-go/internal/sync/singleflight) ([BSD-3-Clause](https://github.com/aws/smithy-go/blob/v1.13.5/internal/sync/singleflight/LICENSE))
- [github.com/coreos/go-iptables/iptables](https://pkg.go.dev/github.com/coreos/go-iptables/iptables) ([Apache-2.0](https://github.com/coreos/go-iptables/blob/v0.6.0/LICENSE))
- [github.com/creack/pty](https://pkg.go.dev/github.com/creack/pty) ([MIT](https://github.com/creack/pty/blob/v1.1.17/LICENSE))
- [github.com/dblohm7/wingoes](https://pkg.go.dev/github.com/dblohm7/wingoes) ([BSD-3-Clause](https://github.com/dblohm7/wingoes/blob/6ac47ab19aa5/LICENSE))
- [github.com/creack/pty](https://pkg.go.dev/github.com/creack/pty) ([MIT](https://github.com/creack/pty/blob/v1.1.18/LICENSE))
- [github.com/dblohm7/wingoes](https://pkg.go.dev/github.com/dblohm7/wingoes) ([BSD-3-Clause](https://github.com/dblohm7/wingoes/blob/111c8c3b57c8/LICENSE))
- [github.com/fxamacker/cbor/v2](https://pkg.go.dev/github.com/fxamacker/cbor/v2) ([MIT](https://github.com/fxamacker/cbor/blob/v2.4.0/LICENSE))
- [github.com/go-ole/go-ole](https://pkg.go.dev/github.com/go-ole/go-ole) ([MIT](https://github.com/go-ole/go-ole/blob/v1.2.6/LICENSE))
- [github.com/godbus/dbus/v5](https://pkg.go.dev/github.com/godbus/dbus/v5) ([BSD-2-Clause](https://github.com/godbus/dbus/blob/v5.0.6/LICENSE))
- [github.com/godbus/dbus/v5](https://pkg.go.dev/github.com/godbus/dbus/v5) ([BSD-2-Clause](https://github.com/godbus/dbus/blob/v5.1.0/LICENSE))
- [github.com/golang/groupcache/lru](https://pkg.go.dev/github.com/golang/groupcache/lru) ([Apache-2.0](https://github.com/golang/groupcache/blob/41bb18bfe9da/LICENSE))
- [github.com/google/btree](https://pkg.go.dev/github.com/google/btree) ([Apache-2.0](https://github.com/google/btree/blob/v1.0.1/LICENSE))
- [github.com/google/btree](https://pkg.go.dev/github.com/google/btree) ([Apache-2.0](https://github.com/google/btree/blob/v1.1.2/LICENSE))
- [github.com/google/uuid](https://pkg.go.dev/github.com/google/uuid) ([BSD-3-Clause](https://github.com/google/uuid/blob/v1.3.0/LICENSE))
- [github.com/hdevalence/ed25519consensus](https://pkg.go.dev/github.com/hdevalence/ed25519consensus) ([BSD-3-Clause](https://github.com/hdevalence/ed25519consensus/blob/c00d1f31bab3/LICENSE))
- [github.com/hdevalence/ed25519consensus](https://pkg.go.dev/github.com/hdevalence/ed25519consensus) ([BSD-3-Clause](https://github.com/hdevalence/ed25519consensus/blob/v0.1.0/LICENSE))
- [github.com/illarion/gonotify](https://pkg.go.dev/github.com/illarion/gonotify) ([MIT](https://github.com/illarion/gonotify/blob/v1.0.1/LICENSE))
- [github.com/insomniacslk/dhcp](https://pkg.go.dev/github.com/insomniacslk/dhcp) ([BSD-3-Clause](https://github.com/insomniacslk/dhcp/blob/de60144f33f8/LICENSE))
- [github.com/insomniacslk/dhcp](https://pkg.go.dev/github.com/insomniacslk/dhcp) ([BSD-3-Clause](https://github.com/insomniacslk/dhcp/blob/974c6f05fe16/LICENSE))
- [github.com/jmespath/go-jmespath](https://pkg.go.dev/github.com/jmespath/go-jmespath) ([Apache-2.0](https://github.com/jmespath/go-jmespath/blob/v0.4.0/LICENSE))
- [github.com/josharian/native](https://pkg.go.dev/github.com/josharian/native) ([MIT](https://github.com/josharian/native/blob/5c7d0dd6ab86/license))
- [github.com/jsimonetti/rtnetlink](https://pkg.go.dev/github.com/jsimonetti/rtnetlink) ([MIT](https://github.com/jsimonetti/rtnetlink/blob/d380b505068b/LICENSE.md))
- [github.com/jsimonetti/rtnetlink](https://pkg.go.dev/github.com/jsimonetti/rtnetlink) ([MIT](https://github.com/jsimonetti/rtnetlink/blob/v1.3.2/LICENSE.md))
- [github.com/kballard/go-shellquote](https://pkg.go.dev/github.com/kballard/go-shellquote) ([MIT](https://github.com/kballard/go-shellquote/blob/95032a82bc51/LICENSE))
- [github.com/klauspost/compress](https://pkg.go.dev/github.com/klauspost/compress) ([Apache-2.0](https://github.com/klauspost/compress/blob/v1.15.4/LICENSE))
- [github.com/klauspost/compress/internal/snapref](https://pkg.go.dev/github.com/klauspost/compress/internal/snapref) ([BSD-3-Clause](https://github.com/klauspost/compress/blob/v1.15.4/internal/snapref/LICENSE))
- [github.com/klauspost/compress/zstd/internal/xxhash](https://pkg.go.dev/github.com/klauspost/compress/zstd/internal/xxhash) ([MIT](https://github.com/klauspost/compress/blob/v1.15.4/zstd/internal/xxhash/LICENSE.txt))
- [github.com/klauspost/compress](https://pkg.go.dev/github.com/klauspost/compress) ([Apache-2.0](https://github.com/klauspost/compress/blob/v1.16.5/LICENSE))
- [github.com/klauspost/compress/internal/snapref](https://pkg.go.dev/github.com/klauspost/compress/internal/snapref) ([BSD-3-Clause](https://github.com/klauspost/compress/blob/v1.16.5/internal/snapref/LICENSE))
- [github.com/klauspost/compress/zstd/internal/xxhash](https://pkg.go.dev/github.com/klauspost/compress/zstd/internal/xxhash) ([MIT](https://github.com/klauspost/compress/blob/v1.16.5/zstd/internal/xxhash/LICENSE.txt))
- [github.com/kortschak/wol](https://pkg.go.dev/github.com/kortschak/wol) ([BSD-3-Clause](https://github.com/kortschak/wol/blob/da482cc4850a/LICENSE))
- [github.com/kr/fs](https://pkg.go.dev/github.com/kr/fs) ([BSD-3-Clause](https://github.com/kr/fs/blob/v0.1.0/LICENSE))
- [github.com/mattn/go-colorable](https://pkg.go.dev/github.com/mattn/go-colorable) ([MIT](https://github.com/mattn/go-colorable/blob/v0.1.12/LICENSE))
- [github.com/mattn/go-isatty](https://pkg.go.dev/github.com/mattn/go-isatty) ([MIT](https://github.com/mattn/go-isatty/blob/v0.0.14/LICENSE))
- [github.com/mdlayher/genetlink](https://pkg.go.dev/github.com/mdlayher/genetlink) ([MIT](https://github.com/mdlayher/genetlink/blob/v1.2.0/LICENSE.md))
- [github.com/mdlayher/netlink](https://pkg.go.dev/github.com/mdlayher/netlink) ([MIT](https://github.com/mdlayher/netlink/blob/v1.7.1/LICENSE.md))
- [github.com/mattn/go-colorable](https://pkg.go.dev/github.com/mattn/go-colorable) ([MIT](https://github.com/mattn/go-colorable/blob/v0.1.13/LICENSE))
- [github.com/mattn/go-isatty](https://pkg.go.dev/github.com/mattn/go-isatty) ([MIT](https://github.com/mattn/go-isatty/blob/v0.0.18/LICENSE))
- [github.com/mdlayher/genetlink](https://pkg.go.dev/github.com/mdlayher/genetlink) ([MIT](https://github.com/mdlayher/genetlink/blob/v1.3.2/LICENSE.md))
- [github.com/mdlayher/netlink](https://pkg.go.dev/github.com/mdlayher/netlink) ([MIT](https://github.com/mdlayher/netlink/blob/v1.7.2/LICENSE.md))
- [github.com/mdlayher/sdnotify](https://pkg.go.dev/github.com/mdlayher/sdnotify) ([MIT](https://github.com/mdlayher/sdnotify/blob/v1.0.0/LICENSE.md))
- [github.com/mdlayher/socket](https://pkg.go.dev/github.com/mdlayher/socket) ([MIT](https://github.com/mdlayher/socket/blob/v0.4.0/LICENSE.md))
- [github.com/mdlayher/socket](https://pkg.go.dev/github.com/mdlayher/socket) ([MIT](https://github.com/mdlayher/socket/blob/v0.4.1/LICENSE.md))
- [github.com/mitchellh/go-ps](https://pkg.go.dev/github.com/mitchellh/go-ps) ([MIT](https://github.com/mitchellh/go-ps/blob/v1.0.0/LICENSE.md))
- [github.com/peterbourgon/ff/v3](https://pkg.go.dev/github.com/peterbourgon/ff/v3) ([Apache-2.0](https://github.com/peterbourgon/ff/blob/v3.1.2/LICENSE))
- [github.com/peterbourgon/ff/v3](https://pkg.go.dev/github.com/peterbourgon/ff/v3) ([Apache-2.0](https://github.com/peterbourgon/ff/blob/v3.3.0/LICENSE))
- [github.com/pierrec/lz4/v4](https://pkg.go.dev/github.com/pierrec/lz4/v4) ([BSD-3-Clause](https://github.com/pierrec/lz4/blob/v4.1.17/LICENSE))
- [github.com/pkg/errors](https://pkg.go.dev/github.com/pkg/errors) ([BSD-2-Clause](https://github.com/pkg/errors/blob/v0.9.1/LICENSE))
- [github.com/pkg/sftp](https://pkg.go.dev/github.com/pkg/sftp) ([BSD-2-Clause](https://github.com/pkg/sftp/blob/v1.13.4/LICENSE))
- [github.com/pkg/sftp](https://pkg.go.dev/github.com/pkg/sftp) ([BSD-2-Clause](https://github.com/pkg/sftp/blob/v1.13.5/LICENSE))
- [github.com/skip2/go-qrcode](https://pkg.go.dev/github.com/skip2/go-qrcode) ([MIT](https://github.com/skip2/go-qrcode/blob/da1b6568686e/LICENSE))
- [github.com/tailscale/certstore](https://pkg.go.dev/github.com/tailscale/certstore) ([MIT](https://github.com/tailscale/certstore/blob/78d6e1c49d8d/LICENSE.md))
- [github.com/tailscale/golang-x-crypto](https://pkg.go.dev/github.com/tailscale/golang-x-crypto) ([BSD-3-Clause](https://github.com/tailscale/golang-x-crypto/blob/bc99ab8c2d17/LICENSE))
- [github.com/tailscale/golang-x-crypto](https://pkg.go.dev/github.com/tailscale/golang-x-crypto) ([BSD-3-Clause](https://github.com/tailscale/golang-x-crypto/blob/17a3db2c30d2/LICENSE))
- [github.com/tailscale/netlink](https://pkg.go.dev/github.com/tailscale/netlink) ([Apache-2.0](https://github.com/tailscale/netlink/blob/cabfb018fe85/LICENSE))
- [github.com/tailscale/wireguard-go](https://pkg.go.dev/github.com/tailscale/wireguard-go) ([MIT](https://github.com/tailscale/wireguard-go/blob/af172621b4dd/LICENSE))
- [github.com/tcnksm/go-httpstat](https://pkg.go.dev/github.com/tcnksm/go-httpstat) ([MIT](https://github.com/tcnksm/go-httpstat/blob/v0.2.0/LICENSE))
- [github.com/toqueteos/webbrowser](https://pkg.go.dev/github.com/toqueteos/webbrowser) ([MIT](https://github.com/toqueteos/webbrowser/blob/v1.2.0/LICENSE.md))
- [github.com/u-root/u-root/pkg/termios](https://pkg.go.dev/github.com/u-root/u-root/pkg/termios) ([BSD-3-Clause](https://github.com/u-root/u-root/blob/948a78c969ad/LICENSE))
- [github.com/u-root/uio](https://pkg.go.dev/github.com/u-root/uio) ([BSD-3-Clause](https://github.com/u-root/uio/blob/c3537552635f/LICENSE))
- [github.com/vishvananda/netlink/nl](https://pkg.go.dev/github.com/vishvananda/netlink/nl) ([Apache-2.0](https://github.com/vishvananda/netlink/blob/650dca95af54/LICENSE))
- [github.com/vishvananda/netns](https://pkg.go.dev/github.com/vishvananda/netns) ([Apache-2.0](https://github.com/vishvananda/netns/blob/50045581ed74/LICENSE))
- [github.com/u-root/u-root/pkg/termios](https://pkg.go.dev/github.com/u-root/u-root/pkg/termios) ([BSD-3-Clause](https://github.com/u-root/u-root/blob/v0.11.0/LICENSE))
- [github.com/u-root/uio](https://pkg.go.dev/github.com/u-root/uio) ([BSD-3-Clause](https://github.com/u-root/uio/blob/3e8cd9d6bf63/LICENSE))
- [github.com/vishvananda/netlink/nl](https://pkg.go.dev/github.com/vishvananda/netlink/nl) ([Apache-2.0](https://github.com/vishvananda/netlink/blob/v1.2.1-beta.2/LICENSE))
- [github.com/vishvananda/netns](https://pkg.go.dev/github.com/vishvananda/netns) ([Apache-2.0](https://github.com/vishvananda/netns/blob/v0.0.4/LICENSE))
- [github.com/x448/float16](https://pkg.go.dev/github.com/x448/float16) ([MIT](https://github.com/x448/float16/blob/v0.8.4/LICENSE))
- [go4.org/mem](https://pkg.go.dev/go4.org/mem) ([Apache-2.0](https://github.com/go4org/mem/blob/927187094b94/LICENSE))
- [go4.org/netipx](https://pkg.go.dev/go4.org/netipx) ([BSD-3-Clause](https://github.com/go4org/netipx/blob/7e7bdc8411bf/LICENSE))
- [golang.org/x/crypto](https://pkg.go.dev/golang.org/x/crypto) ([BSD-3-Clause](https://cs.opensource.google/go/x/crypto/+/v0.6.0:LICENSE))
- [golang.org/x/exp](https://pkg.go.dev/golang.org/x/exp) ([BSD-3-Clause](https://cs.opensource.google/go/x/exp/+/47842c84:LICENSE))
- [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.7.0:LICENSE))
- [go4.org/mem](https://pkg.go.dev/go4.org/mem) ([Apache-2.0](https://github.com/go4org/mem/blob/4f986261bf13/LICENSE))
- [go4.org/netipx](https://pkg.go.dev/go4.org/netipx) ([BSD-3-Clause](https://github.com/go4org/netipx/blob/f1b76eb4bb35/LICENSE))
- [golang.org/x/crypto](https://pkg.go.dev/golang.org/x/crypto) ([BSD-3-Clause](https://cs.opensource.google/go/x/crypto/+/v0.8.0:LICENSE))
- [golang.org/x/exp](https://pkg.go.dev/golang.org/x/exp) ([BSD-3-Clause](https://cs.opensource.google/go/x/exp/+/47ecfdc1:LICENSE))
- [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.9.0:LICENSE))
- [golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2) ([BSD-3-Clause](https://cs.opensource.google/go/x/oauth2/+/v0.7.0:LICENSE))
- [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) ([BSD-3-Clause](https://cs.opensource.google/go/x/sync/+/v0.1.0:LICENSE))
- [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/a3b23cc7:LICENSE))
- [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) ([BSD-3-Clause](https://cs.opensource.google/go/x/term/+/v0.5.0:LICENSE))
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.7.0:LICENSE))
- [golang.org/x/time/rate](https://pkg.go.dev/golang.org/x/time/rate) ([BSD-3-Clause](https://cs.opensource.google/go/x/time/+/579cf78f:LICENSE))
- [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.7.0:LICENSE))
- [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) ([BSD-3-Clause](https://cs.opensource.google/go/x/term/+/v0.7.0:LICENSE))
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.9.0:LICENSE))
- [golang.org/x/time/rate](https://pkg.go.dev/golang.org/x/time/rate) ([BSD-3-Clause](https://cs.opensource.google/go/x/time/+/v0.3.0:LICENSE))
- [golang.zx2c4.com/wintun](https://pkg.go.dev/golang.zx2c4.com/wintun) ([MIT](https://git.zx2c4.com/wintun-go/tree/LICENSE?id=0fa3db229ce2))
- [golang.zx2c4.com/wireguard/windows/tunnel/winipcfg](https://pkg.go.dev/golang.zx2c4.com/wireguard/windows/tunnel/winipcfg) ([MIT](https://git.zx2c4.com/wireguard-windows/tree/COPYING?h=v0.5.3))
- [gopkg.in/yaml.v2](https://pkg.go.dev/gopkg.in/yaml.v2) ([Apache-2.0](https://github.com/go-yaml/yaml/blob/v2.4.0/LICENSE))
- [gvisor.dev/gvisor/pkg](https://pkg.go.dev/gvisor.dev/gvisor/pkg) ([Apache-2.0](https://github.com/google/gvisor/blob/162ed5ef888d/LICENSE))
- [inet.af/peercred](https://pkg.go.dev/inet.af/peercred) ([BSD-3-Clause](https://github.com/inetaf/peercred/blob/0893ea02156a/LICENSE))
- [inet.af/wf](https://pkg.go.dev/inet.af/wf) ([BSD-3-Clause](https://github.com/inetaf/wf/blob/50d96caab2f6/LICENSE))
- [inet.af/wf](https://pkg.go.dev/inet.af/wf) ([BSD-3-Clause](https://github.com/inetaf/wf/blob/36129f591884/LICENSE))
- [k8s.io/client-go/util/homedir](https://pkg.go.dev/k8s.io/client-go/util/homedir) ([Apache-2.0](https://github.com/kubernetes/client-go/blob/v0.25.0/LICENSE))
- [nhooyr.io/websocket](https://pkg.go.dev/nhooyr.io/websocket) ([MIT](https://github.com/nhooyr/websocket/blob/v1.8.7/LICENSE.txt))
- [sigs.k8s.io/yaml](https://pkg.go.dev/sigs.k8s.io/yaml) ([MIT](https://github.com/kubernetes-sigs/yaml/blob/v1.3.0/LICENSE))

View File

@@ -32,8 +32,8 @@ Windows][]. See also the dependencies in the [Tailscale CLI][].
- [github.com/nfnt/resize](https://pkg.go.dev/github.com/nfnt/resize) ([ISC](https://github.com/nfnt/resize/blob/83c6a9932646/LICENSE))
- [github.com/peterbourgon/diskv](https://pkg.go.dev/github.com/peterbourgon/diskv) ([MIT](https://github.com/peterbourgon/diskv/blob/v2.0.1/LICENSE))
- [github.com/skip2/go-qrcode](https://pkg.go.dev/github.com/skip2/go-qrcode) ([MIT](https://github.com/skip2/go-qrcode/blob/da1b6568686e/LICENSE))
- [github.com/tailscale/walk](https://pkg.go.dev/github.com/tailscale/walk) ([BSD-3-Clause](https://github.com/tailscale/walk/blob/f6f2f17d9da1/LICENSE))
- [github.com/tailscale/win](https://pkg.go.dev/github.com/tailscale/win) ([BSD-3-Clause](https://github.com/tailscale/win/blob/ad93eed16885/LICENSE))
- [github.com/tailscale/walk](https://pkg.go.dev/github.com/tailscale/walk) ([BSD-3-Clause](https://github.com/tailscale/walk/blob/f374e3278cd0/LICENSE))
- [github.com/tailscale/win](https://pkg.go.dev/github.com/tailscale/win) ([BSD-3-Clause](https://github.com/tailscale/win/blob/59dfb47dfef1/LICENSE))
- [github.com/tc-hib/winres](https://pkg.go.dev/github.com/tc-hib/winres) ([0BSD](https://github.com/tc-hib/winres/blob/v0.1.6/LICENSE))
- [github.com/x448/float16](https://pkg.go.dev/github.com/x448/float16) ([MIT](https://github.com/x448/float16/blob/v0.8.4/LICENSE))
- [go4.org/mem](https://pkg.go.dev/go4.org/mem) ([Apache-2.0](https://github.com/go4org/mem/blob/4f986261bf13/LICENSE))
@@ -41,12 +41,12 @@ Windows][]. See also the dependencies in the [Tailscale CLI][].
- [golang.org/x/crypto](https://pkg.go.dev/golang.org/x/crypto) ([BSD-3-Clause](https://cs.opensource.google/go/x/crypto/+/v0.6.0:LICENSE))
- [golang.org/x/exp](https://pkg.go.dev/golang.org/x/exp) ([BSD-3-Clause](https://cs.opensource.google/go/x/exp/+/cafedaf6:LICENSE))
- [golang.org/x/image/bmp](https://pkg.go.dev/golang.org/x/image/bmp) ([BSD-3-Clause](https://cs.opensource.google/go/x/image/+/v0.5.0:LICENSE))
- [golang.org/x/mod](https://pkg.go.dev/golang.org/x/mod) ([BSD-3-Clause](https://cs.opensource.google/go/x/mod/+/v0.7.0:LICENSE))
- [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.7.0:LICENSE))
- [golang.org/x/mod](https://pkg.go.dev/golang.org/x/mod) ([BSD-3-Clause](https://cs.opensource.google/go/x/mod/+/v0.10.0:LICENSE))
- [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.9.0:LICENSE))
- [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) ([BSD-3-Clause](https://cs.opensource.google/go/x/sync/+/v0.1.0:LICENSE))
- [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/a3b23cc7:LICENSE))
- [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) ([BSD-3-Clause](https://cs.opensource.google/go/x/term/+/v0.5.0:LICENSE))
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.7.0:LICENSE))
- [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.7.0:LICENSE))
- [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) ([BSD-3-Clause](https://cs.opensource.google/go/x/term/+/v0.7.0:LICENSE))
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.9.0:LICENSE))
- [golang.zx2c4.com/wintun](https://pkg.go.dev/golang.zx2c4.com/wintun) ([MIT](https://git.zx2c4.com/wintun-go/tree/LICENSE?id=0fa3db229ce2))
- [golang.zx2c4.com/wireguard/windows/tunnel/winipcfg](https://pkg.go.dev/golang.zx2c4.com/wireguard/windows/tunnel/winipcfg) ([MIT](https://git.zx2c4.com/wireguard-windows/tree/COPYING?h=v0.5.3))
- [gopkg.in/Knetic/govaluate.v3](https://pkg.go.dev/gopkg.in/Knetic/govaluate.v3) ([MIT](https://github.com/Knetic/govaluate/blob/v3.0.0/LICENSE))

View File

@@ -20,6 +20,7 @@ import (
"tailscale.com/logpolicy"
"tailscale.com/logtail"
"tailscale.com/logtail/filch"
"tailscale.com/net/netmon"
"tailscale.com/net/sockstats"
"tailscale.com/smallzstd"
"tailscale.com/types/logger"
@@ -33,7 +34,7 @@ const pollInterval = time.Second / 10
// logInterval specifies how often to log sockstat events to disk.
// This delay is added to prevent an infinite loop when logs are uploaded,
// which itself creates additional sockstat events.
const logInterval = 3 * time.Second
const logInterval = 10 * time.Second
// maxLogFileSize specifies the maximum size of a log file before it is rotated.
// Our logs are fairly compact, and we are mostly only looking at a few hours of data.
@@ -83,7 +84,7 @@ type event struct {
// SockstatLogID reproducibly derives a new logid.PrivateID for sockstat logging from a node's public backend log ID.
// The returned PrivateID is the sha256 sum of logID + "sockstat".
// If a node's public log ID becomes known, it is trivial to spoof sockstat logs for that node.
// Given the this is just for debugging, we're not too concerned about that.
// Given that this is just for debugging, we're not too concerned about that.
func SockstatLogID(logID logid.PublicID) logid.PrivateID {
return logid.PrivateID(sha256.Sum256([]byte(logID.String() + "sockstat")))
}
@@ -92,7 +93,8 @@ func SockstatLogID(logID logid.PublicID) logid.PrivateID {
// On platforms that do not support sockstat logging, a nil Logger will be returned.
// The returned Logger is not yet enabled, and must be shut down with Shutdown when it is no longer needed.
// Logs will be uploaded to the log server using a new log ID derived from the provided backend logID.
func NewLogger(logdir string, logf logger.Logf, logID logid.PublicID) (*Logger, error) {
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
func NewLogger(logdir string, logf logger.Logf, logID logid.PublicID, netMon *netmon.Monitor) (*Logger, error) {
if !sockstats.IsAvailable {
return nil, nil
}
@@ -112,7 +114,7 @@ func NewLogger(logdir string, logf logger.Logf, logID logid.PublicID) (*Logger,
logger := &Logger{
logf: logf,
filch: filch,
tr: logpolicy.NewLogtailTransport(logtail.DefaultHost),
tr: logpolicy.NewLogtailTransport(logtail.DefaultHost, netMon),
}
logger.logger = logtail.NewLogger(logtail.Config{
BaseURL: logpolicy.LogURL(),

View File

@@ -23,7 +23,7 @@ func TestResourceCleanup(t *testing.T) {
if err != nil {
t.Fatal(err)
}
lg, err := NewLogger(td, logger.Discard, id.Public())
lg, err := NewLogger(td, logger.Discard, id.Public(), nil)
if err != nil {
t.Fatal(err)
}

View File

@@ -37,6 +37,7 @@ import (
"tailscale.com/net/dnscache"
"tailscale.com/net/dnsfallback"
"tailscale.com/net/netknob"
"tailscale.com/net/netmon"
"tailscale.com/net/netns"
"tailscale.com/net/tlsdial"
"tailscale.com/net/tshttpproxy"
@@ -450,14 +451,15 @@ func tryFixLogStateLocation(dir, cmdname string) {
// New returns a new log policy (a logger and its instance ID) for a
// given collection name.
func New(collection string) *Policy {
return NewWithConfigPath(collection, "", "")
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
func New(collection string, netMon *netmon.Monitor) *Policy {
return NewWithConfigPath(collection, "", "", netMon)
}
// NewWithConfigPath is identical to New,
// but uses the specified directory and command name.
// If either is empty, it derives them automatically.
func NewWithConfigPath(collection, dir, cmdName string) *Policy {
func NewWithConfigPath(collection, dir, cmdName string, netMon *netmon.Monitor) *Policy {
var lflags int
if term.IsTerminal(2) || runtime.GOOS == "windows" {
lflags = 0
@@ -554,7 +556,7 @@ func NewWithConfigPath(collection, dir, cmdName string) *Policy {
}
return w
},
HTTPC: &http.Client{Transport: NewLogtailTransport(logtail.DefaultHost)},
HTTPC: &http.Client{Transport: NewLogtailTransport(logtail.DefaultHost, netMon)},
}
if collection == logtail.CollectionNode {
conf.MetricsDelta = clientmetric.EncodeLogTailMetricsDelta
@@ -569,7 +571,7 @@ func NewWithConfigPath(collection, dir, cmdName string) *Policy {
log.Println("You have enabled a non-default log target. Doing without being told to by Tailscale staff or your network administrator will make getting support difficult.")
conf.BaseURL = val
u, _ := url.Parse(val)
conf.HTTPC = &http.Client{Transport: NewLogtailTransport(u.Host)}
conf.HTTPC = &http.Client{Transport: NewLogtailTransport(u.Host, netMon)}
}
filchOptions := filch.Options{
@@ -670,13 +672,22 @@ func (p *Policy) Shutdown(ctx context.Context) error {
return nil
}
// DialContext is a net.Dialer.DialContext specialized for use by logtail.
// MakeDialFunc creates a net.Dialer.DialContext function specialized for use
// by logtail.
// It does the following:
// - If DNS lookup fails, consults the bootstrap DNS list of Tailscale hostnames.
// - If TLS connection fails, try again using LetsEncrypt's built-in root certificate,
// for the benefit of older OS platforms which might not include it.
func DialContext(ctx context.Context, netw, addr string) (net.Conn, error) {
nd := netns.FromDialer(log.Printf, &net.Dialer{
//
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
func MakeDialFunc(netMon *netmon.Monitor) func(ctx context.Context, netw, addr string) (net.Conn, error) {
return func(ctx context.Context, netw, addr string) (net.Conn, error) {
return dialContext(ctx, netw, addr, netMon)
}
}
func dialContext(ctx context.Context, netw, addr string, netMon *netmon.Monitor) (net.Conn, error) {
nd := netns.FromDialer(log.Printf, netMon, &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: netknob.PlatformTCPKeepAlive(),
})
@@ -711,7 +722,8 @@ func DialContext(ctx context.Context, netw, addr string) (net.Conn, error) {
dnsCache := &dnscache.Resolver{
Forward: dnscache.Get().Forward, // use default cache's forwarder
UseLastGood: true,
LookupIPFallback: dnsfallback.Lookup,
LookupIPFallback: dnsfallback.MakeLookupFunc(log.Printf, netMon),
NetMon: netMon,
}
dialer := dnscache.Dialer(nd.DialContext, dnsCache)
c, err = dialer(ctx, netw, addr)
@@ -723,7 +735,8 @@ func DialContext(ctx context.Context, netw, addr string) (net.Conn, error) {
// NewLogtailTransport returns an HTTP Transport particularly suited to uploading
// logs to the given host name. See DialContext for details on how it works.
func NewLogtailTransport(host string) http.RoundTripper {
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
func NewLogtailTransport(host string, netMon *netmon.Monitor) http.RoundTripper {
if inTest() {
return noopPretendSuccessTransport{}
}
@@ -739,7 +752,7 @@ func NewLogtailTransport(host string) http.RoundTripper {
tr.DisableCompression = true
// Log whenever we dial:
tr.DialContext = DialContext
tr.DialContext = MakeDialFunc(netMon)
// We're contacting exactly 1 hostname, so the default's 100
// max idle conns is very high for our needs. Even 2 is

View File

@@ -24,11 +24,11 @@ import (
"tailscale.com/envknob"
"tailscale.com/logtail/backoff"
"tailscale.com/net/interfaces"
"tailscale.com/net/netmon"
"tailscale.com/net/sockstats"
tslogger "tailscale.com/types/logger"
"tailscale.com/types/logid"
"tailscale.com/util/set"
"tailscale.com/wgengine/monitor"
)
// DefaultHost is the default host name to upload logs to when
@@ -179,7 +179,7 @@ type Logger struct {
url string
lowMem bool
skipClientTime bool
linkMonitor *monitor.Mon
netMonitor *netmon.Monitor
buffer Buffer
drainWake chan struct{} // signal to speed up drain
flushDelayFn func() time.Duration // negative or zero return value to upload aggressively, or >0 to batch at this delay
@@ -214,12 +214,12 @@ func (l *Logger) SetVerbosityLevel(level int) {
atomic.StoreInt64(&l.stderrLevel, int64(level))
}
// SetLinkMonitor sets the optional the link monitor.
// SetNetMon sets the optional the network monitor.
//
// It should not be changed concurrently with log writes and should
// only be set once.
func (l *Logger) SetLinkMonitor(lm *monitor.Mon) {
l.linkMonitor = lm
func (l *Logger) SetNetMon(lm *netmon.Monitor) {
l.netMonitor = lm
}
// SetSockstatsLabel sets the label used in sockstat logs to identify network traffic from this logger.
@@ -403,16 +403,16 @@ func (l *Logger) uploading(ctx context.Context) {
}
func (l *Logger) internetUp() bool {
if l.linkMonitor == nil {
if l.netMonitor == nil {
// No way to tell, so assume it is.
return true
}
return l.linkMonitor.InterfaceState().AnyInterfaceUp()
return l.netMonitor.InterfaceState().AnyInterfaceUp()
}
func (l *Logger) awaitInternetUp(ctx context.Context) {
upc := make(chan bool, 1)
defer l.linkMonitor.RegisterChangeCallback(func(changed bool, st *interfaces.State) {
defer l.netMonitor.RegisterChangeCallback(func(changed bool, st *interfaces.State) {
if st.AnyInterfaceUp() {
select {
case upc <- true:

View File

@@ -13,6 +13,7 @@ import (
"golang.org/x/sync/errgroup"
"tailscale.com/net/packet"
"tailscale.com/net/tsaddr"
"tailscale.com/types/netlogtype"
)
@@ -92,6 +93,11 @@ func (s *Statistics) UpdateRxVirtual(b []byte) {
s.updateVirtual(b, true)
}
var (
tailscaleServiceIPv4 = tsaddr.TailscaleServiceIP()
tailscaleServiceIPv6 = tsaddr.TailscaleServiceIPv6()
)
func (s *Statistics) updateVirtual(b []byte, receive bool) {
var p packet.Parsed
p.Decode(b)
@@ -100,6 +106,15 @@ func (s *Statistics) updateVirtual(b []byte, receive bool) {
conn.Src, conn.Dst = conn.Dst, conn.Src
}
// Network logging is defined as traffic between two Tailscale nodes.
// Traffic with the internal Tailscale service is not with another node
// and should not be logged. It also happens to be a high volume
// amount of discrete traffic flows (e.g., DNS lookups).
switch conn.Dst.Addr() {
case tailscaleServiceIPv4, tailscaleServiceIPv6:
return
}
s.mu.Lock()
defer s.mu.Unlock()
cnts, found := s.virtual[conn]

View File

@@ -18,12 +18,12 @@ import (
"golang.org/x/exp/slices"
"tailscale.com/health"
"tailscale.com/net/dns/resolver"
"tailscale.com/net/netmon"
"tailscale.com/net/tsdial"
"tailscale.com/types/dnstype"
"tailscale.com/types/logger"
"tailscale.com/util/clientmetric"
"tailscale.com/util/dnsname"
"tailscale.com/wgengine/monitor"
)
var (
@@ -64,14 +64,15 @@ type Manager struct {
}
// NewManagers created a new manager from the given config.
func NewManager(logf logger.Logf, oscfg OSConfigurator, linkMon *monitor.Mon, dialer *tsdial.Dialer, linkSel resolver.ForwardLinkSelector) *Manager {
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
func NewManager(logf logger.Logf, oscfg OSConfigurator, netMon *netmon.Monitor, dialer *tsdial.Dialer, linkSel resolver.ForwardLinkSelector) *Manager {
if dialer == nil {
panic("nil Dialer")
}
logf = logger.WithPrefix(logf, "dns: ")
m := &Manager{
logf: logf,
resolver: resolver.New(logf, linkMon, linkSel, dialer),
resolver: resolver.New(logf, netMon, linkSel, dialer),
os: oscfg,
}
m.ctx, m.ctxCancel = context.WithCancel(context.Background())

View File

@@ -108,7 +108,7 @@ func TestDNSOverTCP(t *testing.T) {
"bradfitz.ts.com.": "2.3.4.5",
}
for domain, _ := range wantResults {
for domain := range wantResults {
b := mkDNSRequest(domain, dns.TypeA, addEDNS)
binary.Write(c, binary.BigEndian, uint16(len(b)))
c.Write(b)

View File

@@ -502,7 +502,7 @@ func genRandomSubdomains(t *testing.T, n int) []dnsname.FQDN {
for len(domains) < cap(domains) {
l := r.Intn(19) + 1
b := make([]byte, l)
for i, _ := range b {
for i := range b {
b[i] = charset[r.Intn(len(charset))]
}
d := string(b) + ".example.com"

View File

@@ -25,6 +25,7 @@ import (
"tailscale.com/net/dns/publicdns"
"tailscale.com/net/dnscache"
"tailscale.com/net/neterror"
"tailscale.com/net/netmon"
"tailscale.com/net/netns"
"tailscale.com/net/sockstats"
"tailscale.com/net/tsdial"
@@ -34,7 +35,6 @@ import (
"tailscale.com/util/cloudenv"
"tailscale.com/util/dnsname"
"tailscale.com/version"
"tailscale.com/wgengine/monitor"
)
// headerBytes is the number of bytes in a DNS message header.
@@ -176,7 +176,7 @@ type resolverAndDelay struct {
// forwarder forwards DNS packets to a number of upstream nameservers.
type forwarder struct {
logf logger.Logf
linkMon *monitor.Mon
netMon *netmon.Monitor
linkSel ForwardLinkSelector // TODO(bradfitz): remove this when tsdial.Dialer absorbs it
dialer *tsdial.Dialer
@@ -206,10 +206,10 @@ func init() {
rand.Seed(time.Now().UnixNano())
}
func newForwarder(logf logger.Logf, linkMon *monitor.Mon, linkSel ForwardLinkSelector, dialer *tsdial.Dialer) *forwarder {
func newForwarder(logf logger.Logf, netMon *netmon.Monitor, linkSel ForwardLinkSelector, dialer *tsdial.Dialer) *forwarder {
f := &forwarder{
logf: logger.WithPrefix(logf, "forward: "),
linkMon: linkMon,
netMon: netMon,
linkSel: linkSel,
dialer: dialer,
}
@@ -355,7 +355,7 @@ func (f *forwarder) packetListener(ip netip.Addr) (nettype.PacketListenerWithNet
return stdNetPacketListener, nil
}
lc := new(net.ListenConfig)
if err := initListenConfig(lc, f.linkMon, linkName); err != nil {
if err := initListenConfig(lc, f.netMon, linkName); err != nil {
return nil, err
}
return nettype.MakePacketListenerWithNetIP(lc), nil
@@ -380,11 +380,12 @@ func (f *forwarder) getKnownDoHClientForProvider(urlBase string) (c *http.Client
if err != nil {
return nil, false
}
nsDialer := netns.NewDialer(f.logf)
nsDialer := netns.NewDialer(f.logf, f.netMon)
dialer := dnscache.Dialer(nsDialer.DialContext, &dnscache.Resolver{
SingleHost: dohURL.Hostname(),
SingleHostStaticResult: allIPs,
Logf: f.logf,
NetMon: f.netMon,
})
c = &http.Client{
Transport: &http.Transport{
@@ -521,7 +522,7 @@ func (f *forwarder) sendUDP(ctx context.Context, fq *forwardQuery, rr resolverAn
// The 1 extra byte is to detect packet truncation.
out := make([]byte, maxResponseBytes+1)
n, _, err := conn.ReadFrom(out)
n, _, err := conn.ReadFromUDPAddrPort(out)
if err != nil {
if err := ctx.Err(); err != nil {
return nil, err
@@ -763,7 +764,7 @@ func (f *forwarder) forwardWithDestChan(ctx context.Context, query packet, respo
}
}
var initListenConfig func(_ *net.ListenConfig, _ *monitor.Mon, tunName string) error
var initListenConfig func(_ *net.ListenConfig, _ *netmon.Monitor, tunName string) error
// nameFromQuery extracts the normalized query name from bs.
func nameFromQuery(bs []byte) (dnsname.FQDN, error) {

View File

@@ -9,16 +9,16 @@ import (
"errors"
"net"
"tailscale.com/net/netmon"
"tailscale.com/net/netns"
"tailscale.com/wgengine/monitor"
)
func init() {
initListenConfig = initListenConfigNetworkExtension
}
func initListenConfigNetworkExtension(nc *net.ListenConfig, mon *monitor.Mon, tunName string) error {
nif, ok := mon.InterfaceState().Interface[tunName]
func initListenConfigNetworkExtension(nc *net.ListenConfig, netMon *netmon.Monitor, tunName string) error {
nif, ok := netMon.InterfaceState().Interface[tunName]
if !ok {
return errors.New("utun not found")
}

View File

@@ -26,6 +26,7 @@ import (
"tailscale.com/envknob"
"tailscale.com/net/dns/resolvconffile"
"tailscale.com/net/netaddr"
"tailscale.com/net/netmon"
"tailscale.com/net/tsaddr"
"tailscale.com/net/tsdial"
"tailscale.com/syncs"
@@ -34,7 +35,6 @@ import (
"tailscale.com/util/clientmetric"
"tailscale.com/util/cloudenv"
"tailscale.com/util/dnsname"
"tailscale.com/wgengine/monitor"
)
const dnsSymbolicFQDN = "magicdns.localhost-tailscale-daemon."
@@ -179,7 +179,7 @@ func WriteRoutes(w *bufio.Writer, routes map[dnsname.FQDN][]*dnstype.Resolver) {
// it delegates to upstream nameservers if any are set.
type Resolver struct {
logf logger.Logf
linkMon *monitor.Mon // or nil
netMon *netmon.Monitor // or nil
dialer *tsdial.Dialer // non-nil
saveConfigForTests func(cfg Config) // used in tests to capture resolver config
// forwarder forwards requests to upstream nameservers.
@@ -205,20 +205,20 @@ type ForwardLinkSelector interface {
}
// New returns a new resolver.
// linkMon optionally specifies a link monitor to use for socket rebinding.
func New(logf logger.Logf, linkMon *monitor.Mon, linkSel ForwardLinkSelector, dialer *tsdial.Dialer) *Resolver {
// netMon optionally specifies a network monitor to use for socket rebinding.
func New(logf logger.Logf, netMon *netmon.Monitor, linkSel ForwardLinkSelector, dialer *tsdial.Dialer) *Resolver {
if dialer == nil {
panic("nil Dialer")
}
r := &Resolver{
logf: logger.WithPrefix(logf, "resolver: "),
linkMon: linkMon,
netMon: netMon,
closed: make(chan struct{}),
hostToIP: map[dnsname.FQDN][]netip.Addr{},
ipToHost: map[netip.Addr]dnsname.FQDN{},
dialer: dialer,
}
r.forwarder = newForwarder(r.logf, linkMon, linkSel, dialer)
r.forwarder = newForwarder(r.logf, netMon, linkSel, dialer)
return r
}

View File

@@ -22,14 +22,13 @@ import (
"time"
miekdns "github.com/miekg/dns"
"golang.org/x/net/dns/dnsmessage"
dns "golang.org/x/net/dns/dnsmessage"
"tailscale.com/net/netaddr"
"tailscale.com/net/netmon"
"tailscale.com/net/tsdial"
"tailscale.com/tstest"
"tailscale.com/types/dnstype"
"tailscale.com/util/dnsname"
"tailscale.com/wgengine/monitor"
)
var (
@@ -316,7 +315,7 @@ func TestRDNSNameToIPv6(t *testing.T) {
}
func newResolver(t testing.TB) *Resolver {
return New(t.Logf, nil /* no link monitor */, nil /* no link selector */, new(tsdial.Dialer))
return New(t.Logf, nil /* no network monitor */, nil /* no link selector */, new(tsdial.Dialer))
}
func TestResolveLocal(t *testing.T) {
@@ -998,7 +997,7 @@ func TestMarshalResponseFormatError(t *testing.T) {
func TestForwardLinkSelection(t *testing.T) {
configCall := make(chan string, 1)
tstest.Replace(t, &initListenConfig, func(nc *net.ListenConfig, mon *monitor.Mon, tunName string) error {
tstest.Replace(t, &initListenConfig, func(nc *net.ListenConfig, netMon *netmon.Monitor, tunName string) error {
select {
case configCall <- tunName:
return nil
@@ -1121,15 +1120,15 @@ func TestHandleExitNodeDNSQueryWithNetPkg(t *testing.T) {
t.Run("no_such_host", func(t *testing.T) {
res, err := handleExitNodeDNSQueryWithNetPkg(context.Background(), t.Logf, backResolver, &response{
Header: dnsmessage.Header{
Header: dns.Header{
ID: 123,
Response: true,
OpCode: 0, // query
},
Question: dnsmessage.Question{
Name: dnsmessage.MustNewName("nx-domain.test."),
Type: dnsmessage.TypeA,
Class: dnsmessage.ClassINET,
Question: dns.Question{
Name: dns.MustNewName("nx-domain.test."),
Type: dns.TypeA,
Class: dns.ClassINET,
},
})
if err != nil {
@@ -1156,74 +1155,74 @@ func TestHandleExitNodeDNSQueryWithNetPkg(t *testing.T) {
}
tests := []struct {
Type dnsmessage.Type
Type dns.Type
Name string
Check func(t testing.TB, got []byte)
}{
{
Type: dnsmessage.TypeA,
Type: dns.TypeA,
Name: "one-a.test.",
Check: matchPacked("\x00{\x84\x00\x00\x01\x00\x01\x00\x00\x00\x00\x05one-a\x04test\x00\x00\x01\x00\x01\x05one-a\x04test\x00\x00\x01\x00\x01\x00\x00\x02X\x00\x04\x01\x02\x03\x04"),
},
{
Type: dnsmessage.TypeA,
Type: dns.TypeA,
Name: "two-a.test.",
Check: matchPacked("\x00{\x84\x00\x00\x01\x00\x02\x00\x00\x00\x00\x05two-a\x04test\x00\x00\x01\x00\x01\xc0\f\x00\x01\x00\x01\x00\x00\x02X\x00\x04\x01\x02\x03\x04\xc0\f\x00\x01\x00\x01\x00\x00\x02X\x00\x04\x05\x06\a\b"),
},
{
Type: dnsmessage.TypeAAAA,
Type: dns.TypeAAAA,
Name: "one-aaaa.test.",
Check: matchPacked("\x00{\x84\x00\x00\x01\x00\x01\x00\x00\x00\x00\bone-aaaa\x04test\x00\x00\x1c\x00\x01\bone-aaaa\x04test\x00\x00\x1c\x00\x01\x00\x00\x02X\x00\x10\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"),
},
{
Type: dnsmessage.TypeAAAA,
Type: dns.TypeAAAA,
Name: "two-aaaa.test.",
Check: matchPacked("\x00{\x84\x00\x00\x01\x00\x02\x00\x00\x00\x00\btwo-aaaa\x04test\x00\x00\x1c\x00\x01\xc0\f\x00\x1c\x00\x01\x00\x00\x02X\x00\x10\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc0\f\x00\x1c\x00\x01\x00\x00\x02X\x00\x10\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04"),
},
{
Type: dnsmessage.TypePTR,
Type: dns.TypePTR,
Name: "4.3.2.1.in-addr.arpa.",
Check: matchPacked("\x00{\x84\x00\x00\x01\x00\x01\x00\x00\x00\x00\x014\x013\x012\x011\ain-addr\x04arpa\x00\x00\f\x00\x01\x014\x013\x012\x011\ain-addr\x04arpa\x00\x00\f\x00\x01\x00\x00\x02X\x00\t\x03foo\x03com\x00"),
},
{
Type: dnsmessage.TypeCNAME,
Type: dns.TypeCNAME,
Name: "cname.test.",
Check: matchPacked("\x00{\x84\x00\x00\x01\x00\x01\x00\x00\x00\x00\x05cname\x04test\x00\x00\x05\x00\x01\x05cname\x04test\x00\x00\x05\x00\x01\x00\x00\x02X\x00\x10\nthe-target\x03foo\x00"),
},
// No records of various types
{
Type: dnsmessage.TypeA,
Type: dns.TypeA,
Name: "no-records.test.",
Check: matchPacked("\x00{\x84\x03\x00\x01\x00\x00\x00\x00\x00\x00\nno-records\x04test\x00\x00\x01\x00\x01"),
},
{
Type: dnsmessage.TypeAAAA,
Type: dns.TypeAAAA,
Name: "no-records.test.",
Check: matchPacked("\x00{\x84\x03\x00\x01\x00\x00\x00\x00\x00\x00\nno-records\x04test\x00\x00\x1c\x00\x01"),
},
{
Type: dnsmessage.TypeCNAME,
Type: dns.TypeCNAME,
Name: "no-records.test.",
Check: matchPacked("\x00{\x84\x03\x00\x01\x00\x00\x00\x00\x00\x00\nno-records\x04test\x00\x00\x05\x00\x01"),
},
{
Type: dnsmessage.TypeSRV,
Type: dns.TypeSRV,
Name: "no-records.test.",
Check: matchPacked("\x00{\x84\x03\x00\x01\x00\x00\x00\x00\x00\x00\nno-records\x04test\x00\x00!\x00\x01"),
},
{
Type: dnsmessage.TypeTXT,
Type: dns.TypeTXT,
Name: "txt.test.",
Check: matchPacked("\x00{\x84\x00\x00\x01\x00\x03\x00\x00\x00\x00\x03txt\x04test\x00\x00\x10\x00\x01\x03txt\x04test\x00\x00\x10\x00\x01\x00\x00\x02X\x00\t\btxt1=one\x03txt\x04test\x00\x00\x10\x00\x01\x00\x00\x02X\x00\t\btxt2=two\x03txt\x04test\x00\x00\x10\x00\x01\x00\x00\x02X\x00\v\ntxt3=three"),
},
{
Type: dnsmessage.TypeSRV,
Type: dns.TypeSRV,
Name: "srv.test.",
Check: matchPacked("\x00{\x84\x00\x00\x01\x00\x02\x00\x00\x00\x00\x03srv\x04test\x00\x00!\x00\x01\x03srv\x04test\x00\x00!\x00\x01\x00\x00\x02X\x00\x0f\x00\x01\x00\x02\x00\x03\x03foo\x03com\x00\x03srv\x04test\x00\x00!\x00\x01\x00\x00\x02X\x00\x0f\x00\x04\x00\x05\x00\x06\x03bar\x03com\x00"),
},
{
Type: dnsmessage.TypeNS,
Type: dns.TypeNS,
Name: "ns.test.",
Check: matchPacked("\x00{\x84\x00\x00\x01\x00\x02\x00\x00\x00\x00\x02ns\x04test\x00\x00\x02\x00\x01\x02ns\x04test\x00\x00\x02\x00\x01\x00\x00\x02X\x00\t\x03ns1\x03foo\x00\x02ns\x04test\x00\x00\x02\x00\x01\x00\x00\x02X\x00\t\x03ns2\x03bar\x00"),
},
@@ -1232,15 +1231,15 @@ func TestHandleExitNodeDNSQueryWithNetPkg(t *testing.T) {
for _, tt := range tests {
t.Run(fmt.Sprintf("%v_%v", tt.Type, strings.Trim(tt.Name, ".")), func(t *testing.T) {
got, err := handleExitNodeDNSQueryWithNetPkg(context.Background(), t.Logf, backResolver, &response{
Header: dnsmessage.Header{
Header: dns.Header{
ID: 123,
Response: true,
OpCode: 0, // query
},
Question: dnsmessage.Question{
Name: dnsmessage.MustNewName(tt.Name),
Question: dns.Question{
Name: dns.MustNewName(tt.Name),
Type: tt.Type,
Class: dnsmessage.ClassINET,
Class: dns.ClassINET,
},
})
if err != nil {

View File

@@ -21,6 +21,7 @@ import (
"time"
"tailscale.com/envknob"
"tailscale.com/net/netmon"
"tailscale.com/types/logger"
"tailscale.com/util/cloudenv"
"tailscale.com/util/singleflight"
@@ -91,6 +92,11 @@ type Resolver struct {
// be added to all log messages printed with this logger.
Logf logger.Logf
// NetMon optionally provides a netmon.Monitor to use to get the current
// (cached) network interface.
// If nil, the interface will be looked up dynamically.
NetMon *netmon.Monitor
sf singleflight.Group[string, ipRes]
mu sync.Mutex

View File

@@ -13,7 +13,6 @@ import (
"encoding/json"
"errors"
"fmt"
"log"
"net"
"net/http"
"net/netip"
@@ -24,16 +23,25 @@ import (
"time"
"tailscale.com/atomicfile"
"tailscale.com/net/netmon"
"tailscale.com/net/netns"
"tailscale.com/net/tlsdial"
"tailscale.com/net/tshttpproxy"
"tailscale.com/syncs"
"tailscale.com/tailcfg"
"tailscale.com/types/logger"
"tailscale.com/util/slicesx"
)
func Lookup(ctx context.Context, host string) ([]netip.Addr, error) {
// MakeLookupFunc creates a function that can be used to resolve hostnames
// (e.g. as a LookupIPFallback from dnscache.Resolver).
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
func MakeLookupFunc(logf logger.Logf, netMon *netmon.Monitor) func(ctx context.Context, host string) ([]netip.Addr, error) {
return func(ctx context.Context, host string) ([]netip.Addr, error) {
return lookup(ctx, host, logf, netMon)
}
}
func lookup(ctx context.Context, host string, logf logger.Logf, netMon *netmon.Monitor) ([]netip.Addr, error) {
if ip, err := netip.ParseAddr(host); err == nil && ip.IsValid() {
return []netip.Addr{ip}, nil
}
@@ -81,7 +89,7 @@ func Lookup(ctx context.Context, host string) ([]netip.Addr, error) {
logf("trying bootstrapDNS(%q, %q) for %q ...", cand.dnsName, cand.ip, host)
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
dm, err := bootstrapDNSMap(ctx, cand.dnsName, cand.ip, host)
dm, err := bootstrapDNSMap(ctx, cand.dnsName, cand.ip, host, logf, netMon)
if err != nil {
logf("bootstrapDNS(%q, %q) for %q error: %v", cand.dnsName, cand.ip, host, err)
continue
@@ -100,8 +108,8 @@ func Lookup(ctx context.Context, host string) ([]netip.Addr, error) {
// serverName and serverIP of are, say, "derpN.tailscale.com".
// queryName is the name being sought (e.g. "controlplane.tailscale.com"), passed as hint.
func bootstrapDNSMap(ctx context.Context, serverName string, serverIP netip.Addr, queryName string) (dnsMap, error) {
dialer := netns.NewDialer(logf)
func bootstrapDNSMap(ctx context.Context, serverName string, serverIP netip.Addr, queryName string, logf logger.Logf, netMon *netmon.Monitor) (dnsMap, error) {
dialer := netns.NewDialer(logf, netMon)
tr := http.DefaultTransport.(*http.Transport).Clone()
tr.Proxy = tshttpproxy.ProxyFromEnvironment
tr.DialContext = func(ctx context.Context, netw, addr string) (net.Conn, error) {
@@ -194,7 +202,7 @@ var cachePath string
// UpdateCache stores the DERP map cache back to disk.
//
// The caller must not mutate 'c' after calling this function.
func UpdateCache(c *tailcfg.DERPMap) {
func UpdateCache(c *tailcfg.DERPMap, logf logger.Logf) {
// Don't do anything if nothing changed.
curr := cachedDERPMap.Load()
if reflect.DeepEqual(curr, c) {
@@ -227,7 +235,7 @@ func UpdateCache(c *tailcfg.DERPMap) {
//
// This function should be called before any calls to UpdateCache, as it is not
// concurrency-safe.
func SetCachePath(path string) {
func SetCachePath(path string, logf logger.Logf) {
cachePath = path
f, err := os.Open(path)
@@ -246,23 +254,3 @@ func SetCachePath(path string) {
cachedDERPMap.Store(dm)
logf("[v2] dnsfallback: SetCachePath loaded cached DERP map")
}
// logfunc stores the logging function to use for this package.
var logfunc syncs.AtomicValue[logger.Logf]
// SetLogger sets the logging function that this package will use, and returns
// the old value (which may be nil).
//
// If this function is never called, or if this function is called with a nil
// value, 'log.Printf' will be used to print logs.
func SetLogger(log logger.Logf) (old logger.Logf) {
return logfunc.Swap(log)
}
func logf(format string, args ...any) {
if lf := logfunc.Load(); lf != nil {
lf(format, args...)
} else {
log.Printf(format, args...)
}
}

View File

@@ -24,11 +24,6 @@ func TestGetDERPMap(t *testing.T) {
}
func TestCache(t *testing.T) {
oldlog := logfunc.Load()
SetLogger(t.Logf)
t.Cleanup(func() {
SetLogger(oldlog)
})
cacheFile := filepath.Join(t.TempDir(), "cache.json")
// Write initial cache value
@@ -73,7 +68,7 @@ func TestCache(t *testing.T) {
cachedDERPMap.Store(nil)
// Load the cache
SetCachePath(cacheFile)
SetCachePath(cacheFile, t.Logf)
if cm := cachedDERPMap.Load(); !reflect.DeepEqual(initialCache, cm) {
t.Fatalf("cached map was %+v; want %+v", cm, initialCache)
}
@@ -105,11 +100,6 @@ func TestCache(t *testing.T) {
}
func TestCacheUnchanged(t *testing.T) {
oldlog := logfunc.Load()
SetLogger(t.Logf)
t.Cleanup(func() {
SetLogger(oldlog)
})
cacheFile := filepath.Join(t.TempDir(), "cache.json")
// Write initial cache value
@@ -140,7 +130,7 @@ func TestCacheUnchanged(t *testing.T) {
cachedDERPMap.Store(nil)
// Load the cache
SetCachePath(cacheFile)
SetCachePath(cacheFile, t.Logf)
if cm := cachedDERPMap.Load(); !reflect.DeepEqual(initialCache, cm) {
t.Fatalf("cached map was %+v; want %+v", cm, initialCache)
}
@@ -152,7 +142,7 @@ func TestCacheUnchanged(t *testing.T) {
t.Fatal(err)
}
UpdateCache(initialCache)
UpdateCache(initialCache, t.Logf)
if _, err := os.Stat(cacheFile); !os.IsNotExist(err) {
t.Fatalf("got err=%v; expected to not find cache file", err)
}
@@ -173,7 +163,7 @@ func TestCacheUnchanged(t *testing.T) {
clonedNode.IPv4 = "1.2.3.5"
updatedCache.Regions[99].Nodes = append(updatedCache.Regions[99].Nodes, &clonedNode)
UpdateCache(updatedCache)
UpdateCache(updatedCache, t.Logf)
if st, err := os.Stat(cacheFile); err != nil {
t.Fatalf("could not stat cache file; err=%v", err)
} else if !st.Mode().IsRegular() || st.Size() == 0 {

View File

@@ -351,12 +351,6 @@ func (s *State) String() string {
return sb.String()
}
// ChangeFunc is a callback function (usually registered with
// wgengine/monitor's Mon) that's called when the network
// changed. The changed parameter is whether the network changed
// enough for State to have changed since the last callback.
type ChangeFunc func(changed bool, state *State)
// An InterfaceFilter indicates whether EqualFiltered should use i when deciding whether two States are equal.
// ips are all the IPPrefixes associated with i.
type InterfaceFilter func(i Interface, ips []netip.Prefix) bool

View File

@@ -35,6 +35,10 @@ func defaultRoute() (d DefaultRouteDetails, err error) {
return d, nil
}
// ErrNoGatewayIndexFound is returned by DefaultRouteInterfaceIndex when no
// default route is found.
var ErrNoGatewayIndexFound = errors.New("no gateway index found")
// DefaultRouteInterfaceIndex returns the index of the network interface that
// owns the default route. It returns the first IPv4 or IPv6 default route it
// finds (it does not prefer one or the other).
@@ -75,7 +79,7 @@ func DefaultRouteInterfaceIndex() (int, error) {
return rm.Index, nil
}
}
return 0, errors.New("no gateway index found")
return 0, ErrNoGatewayIndexFound
}
func init() {

View File

@@ -98,7 +98,22 @@ func likelyHomeRouterIPLinux() (ret netip.Addr, ok bool) {
}
log.Printf("interfaces: failed to read /proc/net/route: %v", err)
}
return ret, ret.IsValid()
if ret.IsValid() {
return ret, true
}
if lineNum >= maxProcNetRouteRead {
// If we went over our line limit without finding an answer, assume
// we're a big fancy Linux router (or at least not a home system)
// and set the error bit so we stop trying this in the future (and wasting CPU).
// See https://github.com/tailscale/tailscale/issues/7621.
//
// Remember that "likelyHomeRouterIP" exists purely to find the port
// mapping service (UPnP, PMP, PCP) often present on a home router. If we hit
// the route (line) limit without finding an answer, we're unlikely to ever
// find one in the future.
procNetRouteErr.Store(true)
}
return netip.Addr{}, false
}
// Android apps don't have permission to read /proc/net/route, at
@@ -152,8 +167,8 @@ func defaultRoute() (d DefaultRouteDetails, err error) {
// /proc/net/route. Use netlink to find the default route.
//
// TODO(bradfitz): this allocates a fair bit. We should track
// this in wgengine/monitor instead and have
// interfaces.GetState take a link monitor or similar so the
// this in net/interfaces/monitor instead and have
// interfaces.GetState take a netmon.Monitor or similar so the
// routing table can be cached and the monitor's existing
// subscription to route changes can update the cached state,
// rather than querying the whole thing every time like

View File

@@ -224,7 +224,7 @@ func TestStateString(t *testing.T) {
},
},
InterfaceIPs: map[string][]netip.Prefix{
"eth0": []netip.Prefix{
"eth0": {
netip.MustParsePrefix("10.0.0.2/8"),
},
},

View File

@@ -29,6 +29,7 @@ import (
"tailscale.com/net/interfaces"
"tailscale.com/net/netaddr"
"tailscale.com/net/neterror"
"tailscale.com/net/netmon"
"tailscale.com/net/netns"
"tailscale.com/net/ping"
"tailscale.com/net/portmapper"
@@ -39,6 +40,7 @@ import (
"tailscale.com/types/logger"
"tailscale.com/types/nettype"
"tailscale.com/types/opt"
"tailscale.com/types/ptr"
"tailscale.com/util/clientmetric"
"tailscale.com/util/mak"
)
@@ -158,6 +160,11 @@ type Client struct {
// If nil, log.Printf is used.
Logf logger.Logf
// NetMon optionally provides a netmon.Monitor to use to get the current
// (cached) network interface.
// If nil, the interface will be looked up dynamically.
NetMon *netmon.Monitor
// TimeNow, if non-nil, is used instead of time.Now.
TimeNow func() time.Time
@@ -208,8 +215,7 @@ type Client struct {
// reusing an existing UDP connection.
type STUNConn interface {
WriteToUDPAddrPort([]byte, netip.AddrPort) (int, error)
WriteTo([]byte, net.Addr) (int, error)
ReadFrom([]byte) (int, net.Addr, error)
ReadFromUDPAddrPort([]byte) (int, netip.AddrPort, error)
}
func (c *Client) enoughRegions() int {
@@ -518,9 +524,14 @@ func nodeMight4(n *tailcfg.DERPNode) bool {
return ip.Is4()
}
type packetReaderFromCloser interface {
ReadFromUDPAddrPort([]byte) (int, netip.AddrPort, error)
io.Closer
}
// readPackets reads STUN packets from pc until there's an error or ctx is done.
// In either case, it closes pc.
func (c *Client) readPackets(ctx context.Context, pc net.PacketConn) {
func (c *Client) readPackets(ctx context.Context, pc packetReaderFromCloser) {
done := make(chan struct{})
defer close(done)
@@ -534,7 +545,7 @@ func (c *Client) readPackets(ctx context.Context, pc net.PacketConn) {
var buf [64 << 10]byte
for {
n, addr, err := pc.ReadFrom(buf[:])
n, addr, err := pc.ReadFromUDPAddrPort(buf[:])
if err != nil {
if ctx.Err() != nil {
return
@@ -542,16 +553,11 @@ func (c *Client) readPackets(ctx context.Context, pc net.PacketConn) {
c.logf("ReadFrom: %v", err)
return
}
ua, ok := addr.(*net.UDPAddr)
if !ok {
c.logf("ReadFrom: unexpected addr %T", addr)
continue
}
pkt := buf[:n]
if !stun.Is(pkt) {
continue
}
if ap := netaddr.Unmap(ua.AddrPort()); ap.IsValid() {
if ap := netaddr.Unmap(addr); ap.IsValid() {
c.ReceiveSTUNPacket(pkt, ap)
}
}
@@ -865,22 +871,29 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap) (_ *Report,
return c.finishAndStoreReport(rs, dm), nil
}
ifState, err := interfaces.GetState()
if err != nil {
c.logf("[v1] interfaces: %v", err)
return nil, err
var ifState *interfaces.State
if c.NetMon == nil {
directState, err := interfaces.GetState()
if err != nil {
c.logf("[v1] interfaces: %v", err)
return nil, err
} else {
ifState = directState
}
} else {
ifState = c.NetMon.InterfaceState()
}
// See if IPv6 works at all, or if it's been hard disabled at the
// OS level.
v6udp, err := nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf)).ListenPacket(ctx, "udp6", "[::1]:0")
v6udp, err := nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf, c.NetMon)).ListenPacket(ctx, "udp6", "[::1]:0")
if err == nil {
rs.report.OSHasIPv6 = true
v6udp.Close()
}
// Create a UDP4 socket used for sending to our discovered IPv4 address.
rs.pc4Hair, err = nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf)).ListenPacket(ctx, "udp4", ":0")
rs.pc4Hair, err = nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf, c.NetMon)).ListenPacket(ctx, "udp4", ":0")
if err != nil {
c.logf("udp4: %v", err)
return nil, err
@@ -903,12 +916,14 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap) (_ *Report,
// So do that for now. In the future we might want to classify networks
// that do and don't require this separately. But for now help it.
const documentationIP = "203.0.113.1"
rs.pc4Hair.WriteTo([]byte("tailscale netcheck; see https://github.com/tailscale/tailscale/issues/188"), &net.UDPAddr{IP: net.ParseIP(documentationIP), Port: 12345})
rs.pc4Hair.WriteToUDPAddrPort(
[]byte("tailscale netcheck; see https://github.com/tailscale/tailscale/issues/188"),
netip.AddrPortFrom(netip.MustParseAddr(documentationIP), 12345))
if f := c.GetSTUNConn4; f != nil {
rs.pc4 = f()
} else {
u4, err := nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf)).ListenPacket(ctx, "udp4", c.udpBindAddr())
u4, err := nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf, nil)).ListenPacket(ctx, "udp4", c.udpBindAddr())
if err != nil {
c.logf("udp4: %v", err)
return nil, err
@@ -921,7 +936,7 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap) (_ *Report,
if f := c.GetSTUNConn6; f != nil {
rs.pc6 = f()
} else {
u6, err := nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf)).ListenPacket(ctx, "udp6", c.udpBindAddr())
u6, err := nettype.MakePacketListenerWithNetIP(netns.Listener(c.logf, nil)).ListenPacket(ctx, "udp6", c.udpBindAddr())
if err != nil {
c.logf("udp6: %v", err)
} else {
@@ -929,6 +944,16 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap) (_ *Report,
go c.readPackets(ctx, u6)
}
}
// If our interfaces.State suggested we have IPv6 support but then we
// failed to get an IPv6 sending socket (as in
// https://github.com/tailscale/tailscale/issues/7949), then change
// ifState.HaveV6 before we make a probe plan that involves sending IPv6
// packets and thus assuming rs.pc6 is non-nil.
if rs.pc6 == nil {
ifState = ptr.To(*ifState) // shallow clone
ifState.HaveV6 = false
}
}
plan := makeProbePlan(dm, ifState, last)
@@ -1296,10 +1321,7 @@ func (c *Client) measureAllICMPLatency(ctx context.Context, rs *reportState, nee
ctx, done := context.WithTimeout(ctx, icmpProbeTimeout)
defer done()
p, err := ping.New(ctx, c.logf)
if err != nil {
return err
}
p := ping.New(ctx, c.logf, netns.Listener(c.logf, c.NetMon))
defer p.Close()
c.logf("UDP is blocked, trying ICMP")
@@ -1634,6 +1656,7 @@ func (c *Client) nodeAddr(ctx context.Context, n *tailcfg.DERPNode, proto probeP
Forward: net.DefaultResolver,
UseLastGood: true,
Logf: c.logf,
NetMon: c.NetMon,
}
}
resolver := c.resolver

View File

@@ -830,9 +830,6 @@ func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
}
func TestNodeAddrResolve(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("TODO(#7876): test regressed on windows while CI was broken")
}
c := &Client{
Logf: t.Logf,
UDPBindAddr: "127.0.0.1:0",
@@ -852,6 +849,29 @@ func TestNodeAddrResolve(t *testing.T) {
// No IPv4 or IPv6 addrs
}
// Checks whether IPv6 and IPv6 DNS resolution works on this platform.
ipv6Works := func(t *testing.T) bool {
// Verify that we can create an IPv6 socket.
ln, err := net.ListenPacket("udp6", "[::1]:0")
if err != nil {
t.Logf("IPv6 may not work on this machine: %v", err)
return false
}
ln.Close()
// Resolve a hostname that we know has an IPv6 address.
addrs, err := net.DefaultResolver.LookupNetIP(context.Background(), "ip6", "google.com")
if err != nil {
t.Logf("IPv6 DNS resolution error: %v", err)
return false
}
if len(addrs) == 0 {
t.Logf("IPv6 DNS resolution returned no addresses")
return false
}
return true
}
ctx := context.Background()
for _, tt := range []bool{true, false} {
t.Run(fmt.Sprintf("UseDNSCache=%v", tt), func(t *testing.T) {
@@ -869,6 +889,11 @@ func TestNodeAddrResolve(t *testing.T) {
t.Logf("got IPv4 addr: %v", ap)
})
t.Run("IPv6", func(t *testing.T) {
// Skip if IPv6 doesn't work on this machine.
if !ipv6Works(t) {
t.Skipf("IPv6 may not work on this machine")
}
ap := c.nodeAddr(ctx, dn, probeIPv6)
if !ap.IsValid() {
t.Fatal("expected valid AddrPort")

View File

@@ -4,7 +4,7 @@
// Package monitor provides facilities for monitoring network
// interface and route changes. It primarily exists to know when
// portable devices move between different networks.
package monitor
package netmon
import (
"encoding/json"
@@ -48,15 +48,15 @@ type osMon interface {
IsInterestingInterface(iface string) bool
}
// Mon represents a monitoring instance.
type Mon struct {
// Monitor represents a monitoring instance.
type Monitor struct {
logf logger.Logf
om osMon // nil means not supported on this platform
change chan struct{}
stop chan struct{} // closed on Stop
mu sync.Mutex // guards all following fields
cbs set.HandleSet[interfaces.ChangeFunc]
cbs set.HandleSet[ChangeFunc]
ruleDelCB set.HandleSet[RuleDeleteCallback]
ifState *interfaces.State
gwValid bool // whether gw and gwSelfIP are valid
@@ -70,12 +70,17 @@ type Mon struct {
timeJumped bool // whether we need to send a changed=true after a big time jump
}
// ChangeFunc is a callback function registered with Monitor that's called when the
// network changed. The changed parameter is whether the network changed
// enough for State to have changed since the last callback.
type ChangeFunc func(changed bool, state *interfaces.State)
// New instantiates and starts a monitoring instance.
// The returned monitor is inactive until it's started by the Start method.
// Use RegisterChangeCallback to get notified of network changes.
func New(logf logger.Logf) (*Mon, error) {
func New(logf logger.Logf) (*Monitor, error) {
logf = logger.WithPrefix(logf, "monitor: ")
m := &Mon{
m := &Monitor{
logf: logf,
change: make(chan struct{}, 1),
stop: make(chan struct{}),
@@ -102,13 +107,13 @@ func New(logf logger.Logf) (*Mon, error) {
// interfaces.
//
// The returned value is owned by Mon; it must not be modified.
func (m *Mon) InterfaceState() *interfaces.State {
func (m *Monitor) InterfaceState() *interfaces.State {
m.mu.Lock()
defer m.mu.Unlock()
return m.ifState
}
func (m *Mon) interfaceStateUncached() (*interfaces.State, error) {
func (m *Monitor) interfaceStateUncached() (*interfaces.State, error) {
return interfaces.GetState()
}
@@ -117,7 +122,7 @@ func (m *Mon) interfaceStateUncached() (*interfaces.State, error) {
//
// It's the same as interfaces.LikelyHomeRouterIP, but it caches the
// result until the monitor detects a network change.
func (m *Mon) GatewayAndSelfIP() (gw, myIP netip.Addr, ok bool) {
func (m *Monitor) GatewayAndSelfIP() (gw, myIP netip.Addr, ok bool) {
m.mu.Lock()
defer m.mu.Unlock()
if m.gwValid {
@@ -133,7 +138,7 @@ func (m *Mon) GatewayAndSelfIP() (gw, myIP netip.Addr, ok bool) {
// RegisterChangeCallback adds callback to the set of parties to be
// notified (in their own goroutine) when the network state changes.
// To remove this callback, call unregister (or close the monitor).
func (m *Mon) RegisterChangeCallback(callback interfaces.ChangeFunc) (unregister func()) {
func (m *Monitor) RegisterChangeCallback(callback ChangeFunc) (unregister func()) {
m.mu.Lock()
defer m.mu.Unlock()
handle := m.cbs.Add(callback)
@@ -153,7 +158,7 @@ type RuleDeleteCallback func(table uint8, priority uint32)
// RegisterRuleDeleteCallback adds callback to the set of parties to be
// notified (in their own goroutine) when a Linux ip rule is deleted.
// To remove this callback, call unregister (or close the monitor).
func (m *Mon) RegisterRuleDeleteCallback(callback RuleDeleteCallback) (unregister func()) {
func (m *Monitor) RegisterRuleDeleteCallback(callback RuleDeleteCallback) (unregister func()) {
m.mu.Lock()
defer m.mu.Unlock()
handle := m.ruleDelCB.Add(callback)
@@ -166,7 +171,7 @@ func (m *Mon) RegisterRuleDeleteCallback(callback RuleDeleteCallback) (unregiste
// Start starts the monitor.
// A monitor can only be started & closed once.
func (m *Mon) Start() {
func (m *Monitor) Start() {
m.mu.Lock()
defer m.mu.Unlock()
if m.started || m.closed {
@@ -187,7 +192,7 @@ func (m *Mon) Start() {
}
// Close closes the monitor.
func (m *Mon) Close() error {
func (m *Monitor) Close() error {
m.mu.Lock()
if m.closed {
m.mu.Unlock()
@@ -218,7 +223,7 @@ func (m *Mon) Close() error {
// change and re-check the state of the network. Any registered
// ChangeFunc callbacks will be called within the event coalescing
// period (under a fraction of a second).
func (m *Mon) InjectEvent() {
func (m *Monitor) InjectEvent() {
select {
case m.change <- struct{}{}:
default:
@@ -228,7 +233,7 @@ func (m *Mon) InjectEvent() {
}
}
func (m *Mon) stopped() bool {
func (m *Monitor) stopped() bool {
select {
case <-m.stop:
return true
@@ -239,7 +244,7 @@ func (m *Mon) stopped() bool {
// pump continuously retrieves messages from the connection, notifying
// the change channel of changes, and stopping when a stop is issued.
func (m *Mon) pump() {
func (m *Monitor) pump() {
defer m.goroutines.Done()
for !m.stopped() {
msg, err := m.om.Receive()
@@ -263,7 +268,7 @@ func (m *Mon) pump() {
}
}
func (m *Mon) notifyRuleDeleted(rdm ipRuleDeletedMessage) {
func (m *Monitor) notifyRuleDeleted(rdm ipRuleDeletedMessage) {
m.mu.Lock()
defer m.mu.Unlock()
for _, cb := range m.ruleDelCB {
@@ -274,13 +279,13 @@ func (m *Mon) notifyRuleDeleted(rdm ipRuleDeletedMessage) {
// isInterestingInterface reports whether the provided interface should be
// considered when checking for network state changes.
// The ips parameter should be the IPs of the provided interface.
func (m *Mon) isInterestingInterface(i interfaces.Interface, ips []netip.Prefix) bool {
func (m *Monitor) isInterestingInterface(i interfaces.Interface, ips []netip.Prefix) bool {
return m.om.IsInterestingInterface(i.Name) && interfaces.UseInterestingInterfaces(i, ips)
}
// debounce calls the callback function with a delay between events
// and exits when a stop is issued.
func (m *Mon) debounce() {
func (m *Monitor) debounce() {
defer m.goroutines.Done()
for {
select {
@@ -343,7 +348,7 @@ func wallTime() time.Time {
return time.Now().Round(0)
}
func (m *Mon) pollWallTime() {
func (m *Monitor) pollWallTime() {
m.mu.Lock()
defer m.mu.Unlock()
if m.closed {
@@ -365,7 +370,7 @@ const shouldMonitorTimeJump = runtime.GOOS != "android" && runtime.GOOS != "ios"
// checkWallTimeAdvanceLocked reports whether wall time jumped more than 150% of
// pollWallTimeInterval, indicating we probably just came out of sleep. Once a
// time jump is detected it must be reset by calling resetTimeJumpedLocked.
func (m *Mon) checkWallTimeAdvanceLocked() bool {
func (m *Monitor) checkWallTimeAdvanceLocked() bool {
if !shouldMonitorTimeJump {
panic("unreachable") // if callers are correct
}
@@ -378,7 +383,7 @@ func (m *Mon) checkWallTimeAdvanceLocked() bool {
}
// resetTimeJumpedLocked consumes the signal set by checkWallTimeAdvanceLocked.
func (m *Mon) resetTimeJumpedLocked() {
func (m *Monitor) resetTimeJumpedLocked() {
m.timeJumped = false
}

View File

@@ -1,7 +1,7 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package monitor
package netmon
import (
"fmt"
@@ -24,7 +24,7 @@ type unspecifiedMessage struct{}
func (unspecifiedMessage) ignore() bool { return false }
func newOSMon(logf logger.Logf, _ *Mon) (osMon, error) {
func newOSMon(logf logger.Logf, _ *Monitor) (osMon, error) {
fd, err := unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, 0)
if err != nil {
return nil, err

View File

@@ -1,7 +1,7 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package monitor
package netmon
import (
"encoding/hex"

View File

@@ -1,7 +1,7 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package monitor
package netmon
import (
"bufio"
@@ -24,7 +24,7 @@ type devdConn struct {
conn net.Conn
}
func newOSMon(logf logger.Logf, m *Mon) (osMon, error) {
func newOSMon(logf logger.Logf, m *Monitor) (osMon, error) {
conn, err := net.Dial("unixpacket", "/var/run/devd.seqpacket.pipe")
if err != nil {
logf("devd dial error: %v, falling back to polling method", err)

View File

@@ -3,7 +3,7 @@
//go:build !android
package monitor
package netmon
import (
"net"
@@ -44,7 +44,7 @@ type nlConn struct {
addrCache map[uint32]map[netip.Addr]bool
}
func newOSMon(logf logger.Logf, m *Mon) (osMon, error) {
func newOSMon(logf logger.Logf, m *Monitor) (osMon, error) {
conn, err := netlink.Dial(unix.NETLINK_ROUTE, &netlink.Config{
// Routes get us most of the events of interest, but we need
// address as well to cover things like DHCP deciding to give

View File

@@ -1,7 +1,7 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package monitor
package netmon
import (
"net"

View File

@@ -3,13 +3,13 @@
//go:build (!linux && !freebsd && !windows && !darwin) || android
package monitor
package netmon
import (
"tailscale.com/types/logger"
)
func newOSMon(logf logger.Logf, m *Mon) (osMon, error) {
func newOSMon(logf logger.Logf, m *Monitor) (osMon, error) {
return newPollingMon(logf, m)
}

View File

@@ -1,10 +1,11 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package monitor
package netmon
import (
"flag"
"sync/atomic"
"testing"
"time"
@@ -55,7 +56,10 @@ func TestMonitorInjectEvent(t *testing.T) {
}
}
var monitor = flag.String("monitor", "", `go into monitor mode like 'route monitor'; test never terminates. Value can be either "raw" or "callback"`)
var (
monitor = flag.String("monitor", "", `go into monitor mode like 'route monitor'; test never terminates. Value can be either "raw" or "callback"`)
monitorDuration = flag.Duration("monitor-duration", 0, "if non-zero, how long to run TestMonitorMode. Zero means forever.")
)
func TestMonitorMode(t *testing.T) {
switch *monitor {
@@ -71,18 +75,38 @@ func TestMonitorMode(t *testing.T) {
}
switch *monitor {
case "raw":
var closed atomic.Bool
if *monitorDuration != 0 {
t := time.AfterFunc(*monitorDuration, func() {
closed.Store(true)
mon.Close()
})
defer t.Stop()
}
for {
msg, err := mon.om.Receive()
if closed.Load() {
return
}
if err != nil {
t.Fatal(err)
}
t.Logf("msg: %#v", msg)
}
case "callback":
var done <-chan time.Time
if *monitorDuration != 0 {
t := time.NewTimer(*monitorDuration)
defer t.Stop()
done = t.C
}
n := 0
mon.RegisterChangeCallback(func(changed bool, st *interfaces.State) {
n++
t.Logf("cb: changed=%v, ifSt=%v", changed, st)
})
mon.Start()
select {}
<-done
t.Logf("%v callbacks", n)
}
}

View File

@@ -1,7 +1,7 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package monitor
package netmon
import (
"context"
@@ -44,7 +44,7 @@ type winMon struct {
noDeadlockTicker *time.Ticker
}
func newOSMon(logf logger.Logf, _ *Mon) (osMon, error) {
func newOSMon(logf logger.Logf, _ *Monitor) (osMon, error) {
m := &winMon{
logf: logf,
messagec: make(chan eventMessage, 1),

View File

@@ -3,7 +3,7 @@
//go:build !windows && !darwin
package monitor
package netmon
import (
"bytes"
@@ -17,7 +17,7 @@ import (
"tailscale.com/types/logger"
)
func newPollingMon(logf logger.Logf, m *Mon) (osMon, error) {
func newPollingMon(logf logger.Logf, m *Monitor) (osMon, error) {
return &pollingMon{
logf: logf,
m: m,
@@ -30,7 +30,7 @@ func newPollingMon(logf logger.Logf, m *Mon) (osMon, error) {
// of anything to subscribe to.
type pollingMon struct {
logf logger.Logf
m *Mon
m *Monitor
closeOnce sync.Once
stop chan struct{}

View File

@@ -20,6 +20,7 @@ import (
"sync/atomic"
"tailscale.com/net/netknob"
"tailscale.com/net/netmon"
"tailscale.com/types/logger"
)
@@ -55,19 +56,21 @@ func SetDisableBindConnToInterface(v bool) {
// Listener returns a new net.Listener with its Control hook func
// initialized as necessary to run in logical network namespace that
// doesn't route back into Tailscale.
func Listener(logf logger.Logf) *net.ListenConfig {
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
func Listener(logf logger.Logf, netMon *netmon.Monitor) *net.ListenConfig {
if disabled.Load() {
return new(net.ListenConfig)
}
return &net.ListenConfig{Control: control(logf)}
return &net.ListenConfig{Control: control(logf, netMon)}
}
// NewDialer returns a new Dialer using a net.Dialer with its Control
// hook func initialized as necessary to run in a logical network
// namespace that doesn't route back into Tailscale. It also handles
// using a SOCKS if configured in the environment with ALL_PROXY.
func NewDialer(logf logger.Logf) Dialer {
return FromDialer(logf, &net.Dialer{
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
func NewDialer(logf logger.Logf, netMon *netmon.Monitor) Dialer {
return FromDialer(logf, netMon, &net.Dialer{
KeepAlive: netknob.PlatformTCPKeepAlive(),
})
}
@@ -76,11 +79,12 @@ func NewDialer(logf logger.Logf) Dialer {
// network namespace that doesn't route back into Tailscale. It also
// handles using a SOCKS if configured in the environment with
// ALL_PROXY.
func FromDialer(logf logger.Logf, d *net.Dialer) Dialer {
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
func FromDialer(logf logger.Logf, netMon *netmon.Monitor, d *net.Dialer) Dialer {
if disabled.Load() {
return d
}
d.Control = control(logf)
d.Control = control(logf, netMon)
if wrapDialer != nil {
return wrapDialer(d)
}

View File

@@ -10,6 +10,7 @@ import (
"sync"
"syscall"
"tailscale.com/net/netmon"
"tailscale.com/types/logger"
)
@@ -49,7 +50,7 @@ func SetAndroidProtectFunc(f func(fd int) error) {
androidProtectFunc = f
}
func control(logger.Logf) func(network, address string, c syscall.RawConn) error {
func control(logger.Logf, *netmon.Monitor) func(network, address string, c syscall.RawConn) error {
return controlC
}

View File

@@ -19,24 +19,25 @@ import (
"golang.org/x/sys/unix"
"tailscale.com/envknob"
"tailscale.com/net/interfaces"
"tailscale.com/net/netmon"
"tailscale.com/types/logger"
)
func control(logf logger.Logf) func(network, address string, c syscall.RawConn) error {
func control(logf logger.Logf, netMon *netmon.Monitor) func(network, address string, c syscall.RawConn) error {
return func(network, address string, c syscall.RawConn) error {
return controlLogf(logf, network, address, c)
return controlLogf(logf, netMon, network, address, c)
}
}
var bindToInterfaceByRouteEnv = envknob.RegisterBool("TS_BIND_TO_INTERFACE_BY_ROUTE")
var errInterfaceIndexInvalid = errors.New("interface index invalid")
var errInterfaceStateInvalid = errors.New("interface state invalid")
// controlLogf marks c as necessary to dial in a separate network namespace.
//
// It's intentionally the same signature as net.Dialer.Control
// and net.ListenConfig.Control.
func controlLogf(logf logger.Logf, network, address string, c syscall.RawConn) error {
func controlLogf(logf logger.Logf, netMon *netmon.Monitor, network, address string, c syscall.RawConn) error {
if isLocalhost(address) {
// Don't bind to an interface for localhost connections.
return nil
@@ -47,7 +48,7 @@ func controlLogf(logf logger.Logf, network, address string, c syscall.RawConn) e
return nil
}
idx, err := getInterfaceIndex(logf, address)
idx, err := getInterfaceIndex(logf, netMon, address)
if err != nil {
// callee logged
return nil
@@ -56,15 +57,31 @@ func controlLogf(logf logger.Logf, network, address string, c syscall.RawConn) e
return bindConnToInterface(c, network, address, idx, logf)
}
func getInterfaceIndex(logf logger.Logf, address string) (int, error) {
func getInterfaceIndex(logf logger.Logf, netMon *netmon.Monitor, address string) (int, error) {
// Helper so we can log errors.
defaultIdx := func() (int, error) {
idx, err := interfaces.DefaultRouteInterfaceIndex()
if err != nil {
logf("[unexpected] netns: DefaultRouteInterfaceIndex: %v", err)
return -1, err
if netMon == nil {
idx, err := interfaces.DefaultRouteInterfaceIndex()
if err != nil {
// It's somewhat common for there to be no default gateway route
// (e.g. on a phone with no connectivity), don't log those errors
// since they are expected.
if !errors.Is(err, interfaces.ErrNoGatewayIndexFound) {
logf("[unexpected] netns: DefaultRouteInterfaceIndex: %v", err)
}
return -1, err
}
return idx, nil
}
return idx, nil
state := netMon.InterfaceState()
if state == nil {
return -1, errInterfaceStateInvalid
}
if iface, ok := state.Interface[state.DefaultRouteInterface]; ok {
return iface.Index, nil
}
return -1, errInterfaceStateInvalid
}
useRoute := bindToInterfaceByRoute.Load() || bindToInterfaceByRouteEnv()

View File

@@ -34,7 +34,7 @@ func TestGetInterfaceIndex(t *testing.T) {
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
idx, err := getInterfaceIndex(t.Logf, tc.addr)
idx, err := getInterfaceIndex(t.Logf, nil, tc.addr)
if err != nil {
if tc.err == "" {
t.Fatalf("got unexpected error: %v", err)
@@ -68,7 +68,7 @@ func TestGetInterfaceIndex(t *testing.T) {
t.Fatal(err)
}
idx, err := getInterfaceIndex(t.Logf, "100.100.100.100:53")
idx, err := getInterfaceIndex(t.Logf, nil, "100.100.100.100:53")
if err != nil {
t.Fatal(err)
}

View File

@@ -8,10 +8,11 @@ package netns
import (
"syscall"
"tailscale.com/net/netmon"
"tailscale.com/types/logger"
)
func control(logger.Logf) func(network, address string, c syscall.RawConn) error {
func control(logger.Logf, *netmon.Monitor) func(network, address string, c syscall.RawConn) error {
return controlC
}

View File

@@ -15,6 +15,7 @@ import (
"golang.org/x/sys/unix"
"tailscale.com/envknob"
"tailscale.com/net/interfaces"
"tailscale.com/net/netmon"
"tailscale.com/types/logger"
)
@@ -85,7 +86,7 @@ func ignoreErrors() bool {
return false
}
func control(logger.Logf) func(network, address string, c syscall.RawConn) error {
func control(logger.Logf, *netmon.Monitor) func(network, address string, c syscall.RawConn) error {
return controlC
}

View File

@@ -24,7 +24,7 @@ func TestDial(t *testing.T) {
if !*extNetwork {
t.Skip("skipping test without --use-external-network")
}
d := NewDialer(t.Logf)
d := NewDialer(t.Logf, nil)
c, err := d.Dial("tcp", "google.com:80")
if err != nil {
t.Fatal(err)

View File

@@ -12,6 +12,7 @@ import (
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"tailscale.com/net/interfaces"
"tailscale.com/net/netmon"
"tailscale.com/types/logger"
)
@@ -26,7 +27,7 @@ func interfaceIndex(iface *winipcfg.IPAdapterAddresses) uint32 {
return iface.IfIndex
}
func control(logger.Logf) func(network, address string, c syscall.RawConn) error {
func control(logger.Logf, *netmon.Monitor) func(network, address string, c syscall.RawConn) error {
return controlC
}

View File

@@ -558,19 +558,42 @@ func withPort(ap netip.AddrPort, port uint16) netip.AddrPort {
// Currently (2023-03-01) only TCP/UDP/ICMP over IPv4 is supported.
// p is modified in place.
// If p.IPProto is unknown, only the IP header checksum is updated.
// TODO(maisem): more protocols (sctp, gre, dccp)
func updateV4PacketChecksums(p *Parsed, old, new netip.Addr) {
o4, n4 := old.As4(), new.As4()
updateV4Checksum(p.Buffer()[10:12], o4[:], n4[:]) // header
switch p.IPProto {
case ipproto.UDP:
updateV4Checksum(p.Transport()[6:8], o4[:], n4[:])
case ipproto.TCP:
updateV4Checksum(p.Transport()[16:18], o4[:], n4[:])
case ipproto.ICMPv4:
// Nothing to do.
if len(p.Buffer()) < 12 {
// Not enough space for an IPv4 header.
return
}
o4, n4 := old.As4(), new.As4()
// First update the checksum in the IP header.
updateV4Checksum(p.Buffer()[10:12], o4[:], n4[:])
// Now update the transport layer checksums, where applicable.
tr := p.Transport()
switch p.IPProto {
case ipproto.UDP, ipproto.DCCP:
if len(tr) < 8 {
// Not enough space for a UDP header.
return
}
updateV4Checksum(tr[6:8], o4[:], n4[:])
case ipproto.TCP:
if len(tr) < 18 {
// Not enough space for a TCP header.
return
}
updateV4Checksum(tr[16:18], o4[:], n4[:])
case ipproto.GRE:
if len(tr) < 6 {
// Not enough space for a GRE header.
return
}
if tr[0] == 1 { // checksum present
updateV4Checksum(tr[4:6], o4[:], n4[:])
}
case ipproto.SCTP, ipproto.ICMPv4:
// No transport layer update required.
}
// TODO(maisem): more protocols (sctp, gre, dccp)
}
// updateV4Checksum calculates and updates the checksum in the packet buffer for

Some files were not shown because too many files have changed in this diff Show More