all: remove ipv6 check polling

netmon provides ipv6 availability during network event changes, so use
this metadata instead of wasting on polling check.

Further, repeated network errors will force marking ipv6 as disable if
were being enabled, catching a rare case when ipv6 were disabled from
cli or system settings.
This commit is contained in:
Cuong Manh Le
2025-03-11 01:16:36 +07:00
committed by Cuong Manh Le
parent e0d35d8ba2
commit 58c0e4f15a
5 changed files with 36 additions and 31 deletions

View File

@@ -556,6 +556,10 @@ func (p *prog) proxy(ctx context.Context, req *proxyRequest) *proxyResponse {
if errors.As(err, &e) && e.Timeout() {
upstreamConfig.ReBootstrap()
}
// For network error, turn ipv6 off if enabled.
if ctrld.HasIPv6() && (errUrlNetworkError(err) || errNetworkError(err)) {
ctrld.DisableIPv6()
}
}
return nil

View File

@@ -485,7 +485,7 @@ func (uc *UpstreamConfig) setupDOHTransport() {
uc.transport = uc.newDOHTransport(uc.bootstrapIPs6)
case IpStackSplit:
uc.transport4 = uc.newDOHTransport(uc.bootstrapIPs4)
if hasIPv6() {
if HasIPv6() {
uc.transport6 = uc.newDOHTransport(uc.bootstrapIPs6)
} else {
uc.transport6 = uc.transport4
@@ -655,7 +655,7 @@ func (uc *UpstreamConfig) bootstrapIPForDNSType(dnsType uint16) string {
case dns.TypeA:
return pick(uc.bootstrapIPs4)
default:
if hasIPv6() {
if HasIPv6() {
return pick(uc.bootstrapIPs6)
}
return pick(uc.bootstrapIPs4)
@@ -677,7 +677,7 @@ func (uc *UpstreamConfig) netForDNSType(dnsType uint16) (string, string) {
case dns.TypeA:
return "tcp4-tls", "udp4"
default:
if hasIPv6() {
if HasIPv6() {
return "tcp6-tls", "udp6"
}
return "tcp4-tls", "udp4"

View File

@@ -24,7 +24,7 @@ func (uc *UpstreamConfig) setupDOH3Transport() {
uc.http3RoundTripper = uc.newDOH3Transport(uc.bootstrapIPs6)
case IpStackSplit:
uc.http3RoundTripper4 = uc.newDOH3Transport(uc.bootstrapIPs4)
if hasIPv6() {
if HasIPv6() {
uc.http3RoundTripper6 = uc.newDOH3Transport(uc.bootstrapIPs6)
} else {
uc.http3RoundTripper6 = uc.http3RoundTripper4

View File

@@ -17,9 +17,8 @@ import (
)
const (
controldIPv6Test = "ipv6.controld.io"
v4BootstrapDNS = "76.76.2.22:53"
v6BootstrapDNS = "[2606:1a40::22]:53"
v4BootstrapDNS = "76.76.2.22:53"
v6BootstrapDNS = "[2606:1a40::22]:53"
)
var Dialer = &net.Dialer{

50
net.go
View File

@@ -6,6 +6,8 @@ import (
"sync/atomic"
"time"
"tailscale.com/net/netmon"
ctrldnet "github.com/Control-D-Inc/ctrld/internal/net"
)
@@ -14,38 +16,38 @@ var (
ipv6Available atomic.Bool
)
const ipv6ProbingInterval = 10 * time.Second
func hasIPv6() bool {
// HasIPv6 reports whether the current network stack has IPv6 available.
func HasIPv6() bool {
hasIPv6Once.Do(func() {
Log(context.Background(), ProxyLogger.Load().Debug(), "checking for IPv6 availability once")
ProxyLogger.Load().Debug().Msg("checking for IPv6 availability once")
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
val := ctrldnet.IPv6Available(ctx)
ipv6Available.Store(val)
go probingIPv6(context.TODO(), val)
ProxyLogger.Load().Debug().Msgf("ipv6 availability: %v", val)
mon, err := netmon.New(func(format string, args ...any) {})
if err != nil {
ProxyLogger.Load().Debug().Err(err).Msg("failed to monitor IPv6 state")
return
}
mon.RegisterChangeCallback(func(delta *netmon.ChangeDelta) {
old := ipv6Available.Load()
cur := delta.Monitor.InterfaceState().HaveV6
if old != cur {
ProxyLogger.Load().Warn().Msgf("ipv6 availability changed, old: %v, new: %v", old, cur)
} else {
ProxyLogger.Load().Debug().Msg("ipv6 availability does not changed")
}
ipv6Available.Store(cur)
})
mon.Start()
})
return ipv6Available.Load()
}
// TODO(cuonglm): doing poll check natively for supported platforms.
func probingIPv6(ctx context.Context, old bool) {
ticker := time.NewTicker(ipv6ProbingInterval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
cur := ctrldnet.IPv6Available(ctx)
if ipv6Available.CompareAndSwap(old, cur) {
old = cur
}
Log(ctx, ProxyLogger.Load().Debug(), "IPv6 availability: %v", cur)
}()
}
// DisableIPv6 marks IPv6 as unavailable if enabled.
func DisableIPv6() {
if ipv6Available.CompareAndSwap(true, false) {
ProxyLogger.Load().Debug().Msg("turned off IPv6 availability")
}
}