Compare commits

..
Author SHA1 Message Date
patternihaandClaude Opus 5.5 4296be5d05 TUN inbound: Only log IPv6 setup errors on Windows when DisabledComponents turns IPv6 off, with "misconfigtun" blocking IPv6 then
https://github.com/XTLS/Xray-core/pull/7341#issuecomment-6700092032

IPv6 entries are no longer left out. With IPv6 turned off by
DisabledComponents (0x10, the non-tunnel interfaces, the TUN among them,
as in 0xFF), IPv6 is set up as usual, and errors doing so, clearing its
DNS servers included, are only logged; otherwise they exit, also for a
config without IPv6 entries, which replaces the skips of #6874, #7325 and
#7332. "misconfigtun" then blocks IPv6 outside the TUN whatever the routes,
as IPv6 still works until the reboot that applies the value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 05:08:06 +03:30
patternihaandClaude Opus 5.5 4bcd0c6405 TUN inbound: Recommend at least "dns" and "misconfigtun" for autoSystemWfpBlockLeak
https://github.com/XTLS/Xray-core/pull/7332#issuecomment-6683422958

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 00:00:06 +03:30
patternihaandClaude Opus 5.5 8fe26a91d5 TUN inbound: Ignore IPv6 entries on Windows when DisabledComponents turns IPv6 off on all interfaces
https://github.com/XTLS/Xray-core/pull/7332#issuecomment-6676374920

With IPv6 off on all interfaces by DisabledComponents (0x01 and 0x10 both
set, as in 0xFF), the IPv6 entries of gateway, dns and
autoSystemRoutingTable are ignored and the TUN starts with IPv4 only, so
one config works either way. The value takes effect at the next boot only,
so ignored IPv6 routes add "[::/0]:misconfigtun" to autoSystemWfpBlockLeak:
until the reboot, IPv6 the TUN would have taken cannot leave outside it,
also through interfaces connected later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 23:53:26 +03:30
2 changed files with 44 additions and 13 deletions
+3 -3
View File
@@ -223,16 +223,16 @@ To make it start, wintun.dll specific for your Windows/arch must be present next
After the start network adapter with the name you chose in the config will be created in the system, and exist while Xray is running. A Wintun adapter that already has that name is reused.
With IPv6 disabled system-wide (the `DisabledComponents` registry value), the adapter has no IPv6, and the TUN starts with IPv4 only, unless `gateway`, `dns` or `autoSystemRoutingTable` has IPv6 entries, which then keep it from starting.
With IPv6 turned off by the `DisabledComponents` registry value (0x10 set, as in 0xFF), errors setting up IPv6 for `gateway`, `dns` and `autoSystemRoutingTable`, clearing the IPv6 DNS servers when `dns` has none included, are only logged. Otherwise, they keep the TUN from starting.
When `dns` is set, those servers are applied to the adapter. Windows is kept from registering the TUN's addresses in DNS, and its DNS cache is flushed when the TUN starts and stops. Xray's own lookups through the system's DNS servers, like those of the `localhost` DNS server with `autoOutboundsInterface`, skip them, unless another interface uses them as well, as they would lead back into the TUN.
Setting `autoSystemWfpBlockLeak` to at least `["dns"]` is recommended, to block DNS leaks. It needs `autoSystemRoutingTable` (the config is rejected otherwise), and adds Windows Filtering Platform filters that let traffic of every program but Xray itself leave only through the TUN, each kind chosen by a value:
Setting `autoSystemWfpBlockLeak` to at least `["dns", "misconfigtun"]` is recommended, to block DNS leaks and the traffic of an IP version without routes. It needs `autoSystemRoutingTable` (the config is rejected otherwise), and adds Windows Filtering Platform filters that let traffic of every program but Xray itself leave only through the TUN, each kind chosen by a value:
- `"dns"`: stands for `"53"`, `"853"` (DNS over TLS and QUIC) and `"windnsclient"`. Windows also queries the DNS servers of the other interfaces, through those interfaces whatever the routes say, and other programs reach a resolver on the local network (e.g. `192.168.1.1` from DHCP) through its more specific route.
- `"windnsclient"`: Windows' DNS Client service, except for name resolution on the local network (LLMNR, mDNS). Since Windows 11 and Server 2022, it may query over HTTPS or TLS, to any port; earlier versions only query port 53, so there it does nothing.
- `"tcp"`, `"udp"`: that protocol, to all ports, or with `/<port>` or `/<low>-<high>` (1 to 65535) to some, e.g. `"tcp/853"` or `"udp/10-20"`. The ports alone stand for both, e.g. `"53"` or `"10-20"`.
- `"icmp"`: ICMP and ICMPv6, e.g. `ping`.
- `"misconfigtun"`: an IP version without routes in `autoSystemRoutingTable`, which would bypass the TUN.
- `"misconfigtun"`: an IP version without routes in `autoSystemRoutingTable`, which would bypass the TUN. With IPv6 turned off by `DisabledComponents`, it covers IPv6 whatever the routes, as IPv6 setup errors are then only logged, and IPv6 still works until the reboot that applies the value.
- `"all"`: everything, in both directions. Access to the local network then breaks unless it is routed into the TUN as well, and so do incoming connections to other programs, other VPNs, and virtual machines whose NAT runs on the host.
- `"allexceptlan"`: like `"all"`, but traffic to and from the local network stays allowed, e.g. for printers or casting to a TV: private, link-local, multicast and broadcast addresses (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16`, `224.0.0.0/4`, `255.255.255.255`, `fc00::/7`, `fe80::/10`, `ff00::/8`), not global IPv6 ones. It includes `"dns"`, and the other values apply there as well. `"all"` overrides it.
+41 -10
View File
@@ -23,6 +23,7 @@ import (
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/transport/internet"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
"golang.zx2c4.com/wintun"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
)
@@ -98,6 +99,19 @@ func open(name, desc string) (*wintun.Adapter, error) {
return nil, err
}
// ipv6Off tells whether DisabledComponents turns IPv6 off on the non-tunnel
// interfaces (0x10, as 0xFF does), the TUN among them. Windows applies it at
// boot only.
func ipv6Off() bool {
key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters`, registry.QUERY_VALUE)
if err != nil {
return false
}
defer key.Close()
value, _, err := key.GetIntegerValue("DisabledComponents")
return err == nil && value&0x10 != 0
}
func (t *WindowsTun) Start() (err error) {
var address4, address6 bool
addresses := make([]netip.Prefix, 0, len(t.options.Gateway))
@@ -115,7 +129,6 @@ func (t *WindowsTun) Start() (err error) {
for _, ip := range t.options.DNS {
dns = append(dns, netip.MustParseAddr(ip))
}
dns6 := slices.ContainsFunc(dns, netip.Addr.Is6)
var route4, route6 bool
routesMap := make(map[winipcfg.RouteData]struct{})
@@ -140,6 +153,17 @@ func (t *WindowsTun) Start() (err error) {
routesData = append(routesData, &r)
}
// With IPv6 off by DisabledComponents, setting it up may fail, clearing
// its DNS servers too, which is then only logged.
off6 := ipv6Off()
ignored := func(family winipcfg.AddressFamily, err error) bool {
if family != windows.AF_INET6 || !off6 {
return false
}
errors.LogErrorInner(context.Background(), err, "[tun] ignored, as DisabledComponents turns IPv6 off")
return true
}
var retryTimes int
var firstErr error
startOver:
@@ -156,6 +180,9 @@ startOver:
err = t.luid.SetRoutesForFamily(family, routesData)
if err != nil {
firstErr = errors.New("unable to set routes").Base(err)
if ignored(family, firstErr) {
continue
}
if err == windows.ERROR_NOT_FOUND {
goto startOver
}
@@ -166,6 +193,9 @@ startOver:
err = t.luid.SetIPAddressesForFamily(family, addresses)
if err != nil {
firstErr = errors.New("unable to set ips").Base(err)
if ignored(family, firstErr) {
continue
}
if err == windows.ERROR_NOT_FOUND {
goto startOver
}
@@ -174,9 +204,7 @@ startOver:
}
ipif, err := t.luid.IPInterface(family)
if err != nil {
// With IPv6 disabled system-wide (DisabledComponents), the adapter has no
// IPv6 interface at all. Skip the family unless the config asks for it.
if err == windows.ERROR_NOT_FOUND && family == windows.AF_INET6 && !address6 && !route6 && !dns6 {
if ignored(family, err) {
continue
}
return err
@@ -195,6 +223,9 @@ startOver:
err = ipif.Set()
if err != nil {
firstErr = errors.New("unable to set metric and MTU").Base(err)
if ignored(family, firstErr) {
continue
}
if err == windows.ERROR_NOT_FOUND {
goto startOver
}
@@ -202,13 +233,10 @@ startOver:
}
err = t.luid.SetDNS(family, dns, nil)
if err != nil {
// Skip IPv6 the config does not ask for also when setting its DNS
// servers fails, as reported on Windows 11 24H2 with IPv6 disabled
// (Access is denied).
if family == windows.AF_INET6 && !address6 && !route6 && !dns6 {
firstErr = errors.New("unable to set DNS").Base(err)
if ignored(family, firstErr) {
continue
}
firstErr = errors.New("unable to set DNS").Base(err)
if err == windows.ERROR_NOT_FOUND {
goto startOver
}
@@ -265,7 +293,10 @@ startOver:
if err != nil {
return err
}
block := newLeakFilters(leaks, route4, route6)
// With IPv6 off by DisabledComponents, IPv6 setup failures are only logged,
// and IPv6 still works until the reboot that applies it. So "misconfigtun"
// then blocks IPv6 outside the TUN whatever the routes.
block := newLeakFilters(leaks, route4, route6 && !off6)
if route4 || route6 {
dnsClient := func(l leak) bool { return l.name == "windnsclient" }
if slices.ContainsFunc(block, dnsClient) && !dnsClientUsesAnyPort() {