* net/tsaddr: unmap IPv4-mapped IPv6 addrs in IsTailscaleIP
IsTailscaleIP branched on ip.Is4() before checking the CGNAT range, so an
IPv4-mapped IPv6 address (e.g. ::ffff:100.64.0.1) took the IPv6 path and was
tested only against the ULA range, wrongly returning false for a Tailscale
CGNAT address. Unmap at the top so both forms are classified identically;
Unmap is cheap and IsTailscaleIPv4 stays IPv4-only for callers that need it.
Signed-off-by: Brendan Creane <bcreane@gmail.com>
* wgengine/router/osrouter: remove orphaned tailnet addrs on cleanup
The orphan-address sweep added in #20199 ran only inside Router.Set, so the
teardown path (tailscaled --cleanup, and the unconditional cleanup at daemon
start) never removed stale Tailscale addresses a previous instance left on a
persistent tailscale0 -- it only flushed iptables/nftables.
Wire address removal into cleanUp: with no desired config, every Tailscale-range
address on the interface is an orphan, so enumerate and delete them all (IPv4
and IPv6, best-effort) in removeOrphanedAddrsForCleanup.
tailscaleInterfaceAddrs now yields the interface's addresses as an
iter.Seq[netip.Prefix], and the filters compose lazily over it: tailscaleAddrs
(every Tailscale-range address; used by cleanup), deletableAddrs (isDeletableAddr:
Tailscale-range and deletable now, i.e. excluding v6 when v6 is unavailable; used
by the live Set sweep), and orphanedAddrs (drops the desired addresses). The Set
sweep ranges the composed iterator directly, so no throwaway slices are built.
delAddress is made idempotent: it attempts both the loopback-rule teardown and
the address delete and joins their errors, so a missing firewall rule can't leak
the address, and it no longer no-ops on v6 (cleanup relies on that to remove v6
orphans even when this process never brought IPv6 up).
The Set-time sweep is otherwise unchanged; re-running it on network changes
(netmon) for late orphans remains a follow-up (tailscale/corp#43882).
Updates #19974Fixestailscale/corp#44173
Signed-off-by: Brendan Creane <bcreane@gmail.com>
---------
Signed-off-by: Brendan Creane <bcreane@gmail.com>
* wgengine: configure DNS even when router.Set fails
Reconfig configured the router first and returned on any router.Set error,
before the DNS block ran. On a host where router config fails on every
reconfig -- e.g. a tun MTU below 1280 that breaks IPv6, or a kernel missing
netfilter features -- the OS resolver was never told about MagicDNS or the
tailnet search domain, so tailnet names failed to resolve with no DNS error
in the logs.
Record the router error and continue instead of returning on it, still
attempt dns.Set, and join the router, DNS, and VPN-reconfigure errors into
the return value. DNS stays after router config (still needed: some DNS
managers refuse to apply settings before the device has an address); only
the error coupling is broken. Fixes a regression from 84430cdfa (v1.8.0).
Updates #20447
Signed-off-by: Brendan Creane <bcreane@gmail.com>
* wgengine/router/osrouter: gate IPv6 on per-interface support, not just global
getV6Available reported IPv6 usable whenever the netfilter runner reported
global IPv6 support, missing the case where the kernel has IPv6 but has not
enabled it on tailscale0 specifically -- e.g. when the tun MTU is below the
1280-byte IPv6 minimum, so /proc/sys/net/ipv6/conf/tailscale0 never exists
and the v6 address and route adds fail, aborting the whole Set. See #20447.
AND a per-interface check into getV6Available, evaluated per call so a later
Set picks up v6 if the interface gains it. All v6-gated operations funnel
through getV6Available, so Set now skips v6 gracefully instead of erroring.
Also remove the dead r.v6Available field that masked this with its global
name.
Updates #20447
Signed-off-by: Brendan Creane <bcreane@gmail.com>
---------
Signed-off-by: Brendan Creane <bcreane@gmail.com>
Router.Set reconciled tailscale0's addresses only against the in-memory
r.addrs map, which starts empty each run. After a restart the kernel can
still hold the addresses a previous profile put on tailscale0. With no
record of them, Set never removed them, leaving two tailnets' CGNAT
addresses on the interface. That broke connectivity, because the kernel
could source traffic from the wrong IP.
Fix this by scanning the addresses actually on the interface and, after
reconciling the desired set, removing any in Tailscale's CGNAT/ULA ranges
that aren't in the config. Non-Tailscale addresses are never touched,
and IPv6 addresses are skipped when IPv6 is unavailable, since delAddress
no-ops there. To avoid a netlink dump on every Set, the scan runs only on
the first Set and when the desired address set changes.
This also needs the iptables DelLoopbackRule to tolerate a missing rule:
an orphan left by a previous instance never went through AddLoopbackRule
here, and iptables (unlike nftables) errors when deleting an absent
rule, which would otherwise block the address delete.
Fixes#19974
Signed-off-by: Brendan Creane <bcreane@gmail.com>
linuxRouter has two blocks (connmark rules and the CGNAT drop rule) that
gate on cfg.NetfilterMode, the requested config state. This may cause an
error when setNetfilterModeLocked fails, since it may keep assuming this
config is valid.
We now gate both blocks on r.netfilterMode, matching the pattern used by
SNAT, stateful, and loopback paths.
Fixes#19737
Change-Id: Ia6003a082db99c376e662132d725661afbac0ee9
Signed-off-by: Fernando Serboncini <fserb@tailscale.com>
Clients with the newly added node attribute
`"disable-linux-cgnat-drop-rule"` will not automatically drop inbound
traffic on non-Tailscale network interfaces with the source IP in the
CGNAT IP range. This is an initial proof-of-concept for enabling
connectivity with off-Tailnet CGNAT endpoints.
Fixestailscale/corp#36270.
Signed-off-by: Naman Sood <mail@nsood.in>
These test failures were never caught by CI because the package in question
was missing from our privileged tests list. tailscale/corp#40007 covers improving
our process around this.
Fixes#19316
Signed-off-by: Amal Bansode <amal@tailscale.com>
Start using a common helper for tests to declare that they require root.
This is step 1. A later step will then make this helper track which tests were
skipped so a subsequent pass will run these test as root.
Updates tailscale/corp#40007
Change-Id: I4979e1def0fa3691d38c83f48c89aaa443e7f62e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Add a new vet analyzer that checks t.Run subtest names don't contain
characters requiring quoting when re-running via "go test -run". This
enforces the style guide rule: don't use spaces or punctuation in
subtest names.
The analyzer flags:
- Direct t.Run calls with string literal names containing spaces,
regex metacharacters, quotes, or other problematic characters
- Table-driven t.Run(tt.name, ...) calls where tt ranges over a
slice/map literal with bad name field values
Also fix all 978 existing violations across 81 test files, replacing
spaces with hyphens and shortening long sentence-like names to concise
hyphenated forms.
Updates #19242
Change-Id: Ib0ad96a111bd8e764582d1d4902fe2599454ab65
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
I omitted a lot of the min/max modernizers because they didn't
result in more clear code.
Some of it's older "for x := range 123".
Also: errors.AsType, any, fmt.Appendf, etc.
Updates #18682
Change-Id: I83a451577f33877f962766a5b65ce86f7696471c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
When a Linux system acts as an exit node or subnet router with strict
reverse path filtering (rp_filter=1), reply packets may
be dropped because they fail the RPF check. Reply packets arrive on the
WAN interface but the routing table indicates they should have arrived
on the Tailscale interface, causing the kernel to drop them.
This adds firewall rules in the mangle table to save outbound packet
marks to conntrack and restore them on reply packets before the routing
decision. When reply packets have their marks restored, the kernel uses
the correct routing table (based on the mark) and the packets pass the
rp_filter check.
Implementation adds two rules per address family (IPv4/IPv6):
- mangle/OUTPUT: Save packet marks to conntrack for NEW connections
with non-zero marks in the Tailscale fwmark range (0xff0000)
- mangle/PREROUTING: Restore marks from conntrack to packets for
ESTABLISHED,RELATED connections before routing decision and rp_filter
check
The workaround is automatically enabled when UseConnmarkForRPFilter is
set in the router configuration, which happens when subnet routes are
advertised on Linux systems.
Both iptables and nftables implementations are provided, with automatic
backend detection.
Fixes#3310Fixes#14409Fixes#12022Fixes#15815Fixes#9612
Signed-off-by: Mike O'Driscoll <mikeo@tailscale.com>
This file was never truly necessary and has never actually been used in
the history of Tailscale's open source releases.
A Brief History of AUTHORS files
---
The AUTHORS file was a pattern developed at Google, originally for
Chromium, then adopted by Go and a bunch of other projects. The problem
was that Chromium originally had a copyright line only recognizing
Google as the copyright holder. Because Google (and most open source
projects) do not require copyright assignemnt for contributions, each
contributor maintains their copyright. Some large corporate contributors
then tried to add their own name to the copyright line in the LICENSE
file or in file headers. This quickly becomes unwieldy, and puts a
tremendous burden on anyone building on top of Chromium, since the
license requires that they keep all copyright lines intact.
The compromise was to create an AUTHORS file that would list all of the
copyright holders. The LICENSE file and source file headers would then
include that list by reference, listing the copyright holder as "The
Chromium Authors".
This also become cumbersome to simply keep the file up to date with a
high rate of new contributors. Plus it's not always obvious who the
copyright holder is. Sometimes it is the individual making the
contribution, but many times it may be their employer. There is no way
for the proejct maintainer to know.
Eventually, Google changed their policy to no longer recommend trying to
keep the AUTHORS file up to date proactively, and instead to only add to
it when requested: https://opensource.google/docs/releasing/authors.
They are also clear that:
> Adding contributors to the AUTHORS file is entirely within the
> project's discretion and has no implications for copyright ownership.
It was primarily added to appease a small number of large contributors
that insisted that they be recognized as copyright holders (which was
entirely their right to do). But it's not truly necessary, and not even
the most accurate way of identifying contributors and/or copyright
holders.
In practice, we've never added anyone to our AUTHORS file. It only lists
Tailscale, so it's not really serving any purpose. It also causes
confusion because Tailscalars put the "Tailscale Inc & AUTHORS" header
in other open source repos which don't actually have an AUTHORS file, so
it's ambiguous what that means.
Instead, we just acknowledge that the contributors to Tailscale (whoever
they are) are copyright holders for their individual contributions. We
also have the benefit of using the DCO (developercertificate.org) which
provides some additional certification of their right to make the
contribution.
The source file changes were purely mechanical with:
git ls-files | xargs sed -i -e 's/\(Tailscale Inc &\) AUTHORS/\1 contributors/g'
Updates #cleanup
Change-Id: Ia101a4a3005adb9118051b3416f5a64a4a45987d
Signed-off-by: Will Norris <will@tailscale.com>
So wgengine/router is just the docs + entrypoint + types, and then
underscore importing wgengine/router/osrouter registers the constructors
with the wgengine/router package.
Then tsnet can not pull those in.
Updates #17313
Change-Id: If313226f6987d709ea9193c8f16a909326ceefe7
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>