Skip to content

Commit 76256d2

Browse files
committed
wgengine/router: windows: set SkipAsSource on IPv6 LL addresses
Link-local addresses on the Tailscale interface are not routable. Ideally they would be removed, however, a concern exists that the operating system will attempt to re-add them which would lead to thrashing. Setting SkipAsSource attempts to avoid production of packets using the address as a source in any default behaviors. Before, in powershell: `ping (hostname)` would ping the link-local address of the Tailscale interface, and fail. After: `ping (hostname)` now pings the link-local address on the next highest priority metric local interface. Fixes tailscale#4647 Signed-off-by: James Tucker <james@tailscale.com>
1 parent 8c5c87b commit 76256d2

File tree

2 files changed

+31
-27
lines changed

2 files changed

+31
-27
lines changed

wgengine/router/ifconfig_windows.go

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -574,26 +574,8 @@ func deltaNets(a, b []*net.IPNet) (add, del []*net.IPNet) {
574574
return
575575
}
576576

577-
func excludeIPv6LinkLocal(in []*net.IPNet) (out []*net.IPNet) {
578-
out = in[:0]
579-
for _, n := range in {
580-
if len(n.IP) == 16 && n.IP.IsLinkLocalUnicast() {
581-
// Windows creates a fixed link-local address for wintun,
582-
// which doesn't seem to route correctly. Unfortunately, LLMNR returns this
583-
// address for lookups by the hostname, and Windows prefers using it.
584-
// This means that local traffic addressed to the machine's hostname breaks.
585-
//
586-
// While we otherwise preserve link-local addresses, we delete
587-
// this one to force lookups to use a working address.
588-
//
589-
// See: https://github.com/tailscale/tailscale/issues/4647
590-
if ip, ok := netaddr.FromStdIP(n.IP); !ok || wintunLinkLocal != ip {
591-
continue // filter this IPNet
592-
}
593-
}
594-
out = append(out, n)
595-
}
596-
return out
577+
func isIPv6LinkLocal(in *net.IPNet) bool {
578+
return len(in.IP) == 16 && in.IP.IsLinkLocalUnicast()
597579
}
598580

599581
// ipAdapterUnicastAddressToIPNet converts windows.IpAdapterUnicastAddress to net.IPNet.
@@ -622,14 +604,27 @@ func unicastIPNets(ifc *winipcfg.IPAdapterAddresses) []*net.IPNet {
622604
// doing the minimum number of AddAddresses & DeleteAddress calls.
623605
// This avoids the full FlushAddresses.
624606
//
625-
// Any IPv6 link-local addresses are not deleted.
607+
// Any IPv6 link-local addresses are not deleted out of caution as some
608+
// configurations may repeatedly re-add them. Link-local addresses are adjusted
609+
// to set SkipAsSource. SkipAsSource prevents the addresses from being addded to
610+
// DNS locally or remotely and from being picked as a source address for
611+
// outgoing packets with unspecified sources. See #4647 and
612+
// https://web.archive.org/web/20200912120956/https://devblogs.microsoft.com/scripting/use-powershell-to-change-ip-behavior-with-skipassource/
626613
func syncAddresses(ifc *winipcfg.IPAdapterAddresses, want []*net.IPNet) error {
627614
var erracc error
628615

629616
got := unicastIPNets(ifc)
630617
add, del := deltaNets(got, want)
631-
del = excludeIPv6LinkLocal(del)
618+
619+
ll := make([]*net.IPNet, 0)
632620
for _, a := range del {
621+
// do not delete link-local addresses, and collect them for later
622+
// applying SkipAsSource.
623+
if isIPv6LinkLocal(a) {
624+
ll = append(ll, a)
625+
continue
626+
}
627+
633628
err := ifc.LUID.DeleteIPAddress(*a)
634629
if err != nil {
635630
erracc = fmt.Errorf("deleting IP %q: %w", *a, err)
@@ -643,6 +638,20 @@ func syncAddresses(ifc *winipcfg.IPAdapterAddresses, want []*net.IPNet) error {
643638
}
644639
}
645640

641+
for _, a := range ll {
642+
mib, err := ifc.LUID.IPAddress(a.IP)
643+
if err != nil {
644+
erracc = fmt.Errorf("setting skip-as-source on IP %q: unable to retrieve MIB: %w", *a, err)
645+
continue
646+
}
647+
if !mib.SkipAsSource {
648+
mib.SkipAsSource = true
649+
if err := mib.Set(); err != nil {
650+
erracc = fmt.Errorf("setting skip-as-source on IP %q: unable to set MIB: %w", *a, err)
651+
}
652+
}
653+
}
654+
646655
return erracc
647656
}
648657

wgengine/router/ifconfig_windows_test.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,6 @@ func TestDeltaNets(t *testing.T) {
163163
b: nets("100.84.36.11/32[4]"),
164164
wantDel: nets("fe80::99d0:ec2d:b2e7:536b/64"),
165165
},
166-
{
167-
a: excludeIPv6LinkLocal(nets("100.84.36.11/32", "fe80::99d0:ec2d:b2e7:536b/64")),
168-
b: nets("100.84.36.11/32"),
169-
wantDel: nets("fe80::99d0:ec2d:b2e7:536b/64"),
170-
},
171166
{
172167
a: []*net.IPNet{
173168
{

0 commit comments

Comments
 (0)