Selaa lähdekoodia

net/netcheck: test for OS IPv6 support as well as connectivity.

This lets us distinguish "no IPv6 because the device's ISP doesn't
offer IPv6" from "IPv6 is unavailable/disabled in the OS".

Signed-off-by: David Anderson <[email protected]>
David Anderson 3 vuotta sitten
vanhempi
sitoutus
c1cb3efbba

+ 3 - 1
cmd/tailscale/cli/netcheck.go

@@ -126,8 +126,10 @@ func printReport(dm *tailcfg.DERPMap, report *netcheck.Report) error {
 		printf("\t* IPv6: yes, %v\n", report.GlobalV6)
 	} else if report.IPv6 {
 		printf("\t* IPv6: (no addr found)\n")
+	} else if report.OSHasIPv6 {
+		printf("\t* IPv6: no, but OS has support\n")
 	} else {
-		printf("\t* IPv6: no\n")
+		printf("\t* IPv6: no, unavailable in OS\n")
 	}
 	printf("\t* MappingVariesByDestIP: %v\n", report.MappingVariesByDestIP)
 	printf("\t* HairPinning: %v\n", report.HairPinning)

+ 9 - 0
net/netcheck/netcheck.go

@@ -76,6 +76,7 @@ type Report struct {
 	IPv4        bool // an IPv4 STUN round trip completed
 	IPv6CanSend bool // an IPv6 packet was able to be sent
 	IPv4CanSend bool // an IPv4 packet was able to be sent
+	OSHasIPv6   bool // could bind a socket to ::1
 
 	// MappingVariesByDestIP is whether STUN results depend which
 	// STUN server you're talking to (on IPv4).
@@ -806,6 +807,14 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap) (_ *Report,
 		return nil, err
 	}
 
+	// See if IPv6 works at all, or if it's been hard disabled at the
+	// OS level.
+	v6udp, err := netns.Listener(c.logf).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 = netns.Listener(c.logf).ListenPacket(ctx, "udp4", ":0")
 	if err != nil {

+ 3 - 0
net/netcheck/netcheck_test.go

@@ -111,6 +111,9 @@ func TestWorksWhenUDPBlocked(t *testing.T) {
 	// That's not relevant to this test, so just accept what we're
 	// given.
 	want.IPv4CanSend = r.IPv4CanSend
+	// OS IPv6 test is irrelevant here, accept whatever the current
+	// machine has.
+	want.OSHasIPv6 = r.OSHasIPv6
 
 	if !reflect.DeepEqual(r, want) {
 		t.Errorf("mismatch\n got: %+v\nwant: %+v\n", r, want)

+ 7 - 2
tailcfg/tailcfg.go

@@ -496,10 +496,14 @@ type NetInfo struct {
 	// It reports true even if there's no NAT involved.
 	HairPinning opt.Bool
 
-	// WorkingIPv6 is whether IPv6 works.
+	// WorkingIPv6 is whether the host has IPv6 internet connectivity.
 	WorkingIPv6 opt.Bool
 
-	// WorkingUDP is whether UDP works.
+	// OSHasIPv6 is whether the OS supports IPv6 at all, regardless of
+	// whether IPv6 internet connectivity is available.
+	OSHasIPv6 opt.Bool
+
+	// WorkingUDP is whether the host has UDP internet connectivity.
 	WorkingUDP opt.Bool
 
 	// HavePortMap is whether we have an existing portmap open
@@ -590,6 +594,7 @@ func (ni *NetInfo) BasicallyEqual(ni2 *NetInfo) bool {
 	return ni.MappingVariesByDestIP == ni2.MappingVariesByDestIP &&
 		ni.HairPinning == ni2.HairPinning &&
 		ni.WorkingIPv6 == ni2.WorkingIPv6 &&
+		ni.OSHasIPv6 == ni2.OSHasIPv6 &&
 		ni.WorkingUDP == ni2.WorkingUDP &&
 		ni.HavePortMap == ni2.HavePortMap &&
 		ni.UPnP == ni2.UPnP &&

+ 1 - 0
tailcfg/tailcfg_clone.go

@@ -154,6 +154,7 @@ var _NetInfoCloneNeedsRegeneration = NetInfo(struct {
 	MappingVariesByDestIP opt.Bool
 	HairPinning           opt.Bool
 	WorkingIPv6           opt.Bool
+	OSHasIPv6             opt.Bool
 	WorkingUDP            opt.Bool
 	HavePortMap           bool
 	UPnP                  opt.Bool

+ 1 - 0
tailcfg/tailcfg_test.go

@@ -500,6 +500,7 @@ func TestNetInfoFields(t *testing.T) {
 		"MappingVariesByDestIP",
 		"HairPinning",
 		"WorkingIPv6",
+		"OSHasIPv6",
 		"WorkingUDP",
 		"HavePortMap",
 		"UPnP",

+ 2 - 0
tailcfg/tailcfg_view.go

@@ -338,6 +338,7 @@ func (v *NetInfoView) UnmarshalJSON(b []byte) error {
 func (v NetInfoView) MappingVariesByDestIP() opt.Bool { return v.ж.MappingVariesByDestIP }
 func (v NetInfoView) HairPinning() opt.Bool           { return v.ж.HairPinning }
 func (v NetInfoView) WorkingIPv6() opt.Bool           { return v.ж.WorkingIPv6 }
+func (v NetInfoView) OSHasIPv6() opt.Bool             { return v.ж.OSHasIPv6 }
 func (v NetInfoView) WorkingUDP() opt.Bool            { return v.ж.WorkingUDP }
 func (v NetInfoView) HavePortMap() bool               { return v.ж.HavePortMap }
 func (v NetInfoView) UPnP() opt.Bool                  { return v.ж.UPnP }
@@ -354,6 +355,7 @@ var _NetInfoViewNeedsRegeneration = NetInfo(struct {
 	MappingVariesByDestIP opt.Bool
 	HairPinning           opt.Bool
 	WorkingIPv6           opt.Bool
+	OSHasIPv6             opt.Bool
 	WorkingUDP            opt.Bool
 	HavePortMap           bool
 	UPnP                  opt.Bool