mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-09-23 01:27:04 +00:00
TUN inbound: Refine Windows support (#6478)
https://github.com/XTLS/Xray-core/pull/6478#issuecomment-5489052152
This commit is contained in:
+145
-137
@@ -3,14 +3,14 @@
|
|||||||
package tun
|
package tun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
go_errors "errors"
|
go_errors "errors"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
@@ -31,13 +31,14 @@ func procyield(cycles uint32)
|
|||||||
type WindowsTun struct {
|
type WindowsTun struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
|
|
||||||
options *Config
|
options *Config
|
||||||
adapter *wintun.Adapter
|
adapter *wintun.Adapter
|
||||||
session wintun.Session
|
session wintun.Session
|
||||||
readWait windows.Handle
|
readWait windows.Handle
|
||||||
luid winipcfg.LUID
|
luid winipcfg.LUID
|
||||||
changeCallback winipcfg.ChangeCallback
|
cbr winipcfg.ChangeCallback
|
||||||
closed bool
|
cbi winipcfg.ChangeCallback
|
||||||
|
closed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// WindowsTun implements Tun
|
// WindowsTun implements Tun
|
||||||
@@ -85,23 +86,37 @@ func open(name, desc string) (*wintun.Adapter, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *WindowsTun) Start() error {
|
func (t *WindowsTun) Start() (err error) {
|
||||||
var has4, has6 bool
|
var address4, address6 bool
|
||||||
allowedIPs := make([]netip.Prefix, 0, len(t.options.AutoSystemRoutingTable))
|
addresses := make([]netip.Prefix, 0, len(t.options.Gateway))
|
||||||
for _, route := range t.options.AutoSystemRoutingTable {
|
for _, cidr := range t.options.Gateway {
|
||||||
allowedIPs = append(allowedIPs, netip.MustParsePrefix(route))
|
prefix := netip.MustParsePrefix(cidr)
|
||||||
|
if prefix.Addr().Is4() {
|
||||||
|
address4 = true
|
||||||
|
} else {
|
||||||
|
address6 = true
|
||||||
|
}
|
||||||
|
addresses = append(addresses, prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dns := make([]netip.Addr, 0, len(t.options.DNS))
|
||||||
|
for _, ip := range t.options.DNS {
|
||||||
|
dns = append(dns, netip.MustParseAddr(ip))
|
||||||
|
}
|
||||||
|
|
||||||
|
var route4, route6 bool
|
||||||
routesMap := make(map[winipcfg.RouteData]struct{})
|
routesMap := make(map[winipcfg.RouteData]struct{})
|
||||||
for _, ip := range allowedIPs {
|
for _, cidr := range t.options.AutoSystemRoutingTable {
|
||||||
|
prefix := netip.MustParsePrefix(cidr)
|
||||||
route := winipcfg.RouteData{
|
route := winipcfg.RouteData{
|
||||||
Destination: ip.Masked(),
|
Destination: prefix.Masked(),
|
||||||
Metric: 0,
|
Metric: 0,
|
||||||
}
|
}
|
||||||
if ip.Addr().Is4() {
|
if prefix.Addr().Is4() {
|
||||||
has4 = true
|
route4 = true
|
||||||
route.NextHop = netip.IPv4Unspecified()
|
route.NextHop = netip.IPv4Unspecified()
|
||||||
} else {
|
} else {
|
||||||
has6 = true
|
route6 = true
|
||||||
route.NextHop = netip.IPv6Unspecified()
|
route.NextHop = netip.IPv6Unspecified()
|
||||||
}
|
}
|
||||||
routesMap[route] = struct{}{}
|
routesMap[route] = struct{}{}
|
||||||
@@ -111,24 +126,40 @@ func (t *WindowsTun) Start() error {
|
|||||||
r := route
|
r := route
|
||||||
routesData = append(routesData, &r)
|
routesData = append(routesData, &r)
|
||||||
}
|
}
|
||||||
err := t.luid.SetRoutes(routesData)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("unable to set routes").Base(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(t.options.Gateway) > 0 {
|
var retryTimes int
|
||||||
addresses := make([]netip.Prefix, 0, len(t.options.Gateway))
|
var firstErr error
|
||||||
for _, address := range t.options.Gateway {
|
startOver:
|
||||||
addresses = append(addresses, netip.MustParsePrefix(address))
|
if retryTimes > 0 {
|
||||||
}
|
if retryTimes > 15 {
|
||||||
err := t.luid.SetIPAddresses(addresses)
|
return windows.ERROR_NOT_FOUND
|
||||||
if err != nil {
|
|
||||||
return errors.New("unable to set ips").Base(err)
|
|
||||||
}
|
}
|
||||||
|
errors.LogErrorInner(context.Background(), firstErr, "Interface configuration failed, retrying attempt ", retryTimes, "/15")
|
||||||
|
time.Sleep(time.Second)
|
||||||
}
|
}
|
||||||
|
retryTimes++
|
||||||
if has4 {
|
for _, family := range []winipcfg.AddressFamily{windows.AF_INET, windows.AF_INET6} {
|
||||||
ipif, err := t.luid.IPInterface(windows.AF_INET)
|
if family == windows.AF_INET && route4 || family == windows.AF_INET6 && route6 {
|
||||||
|
err = t.luid.SetRoutesForFamily(family, routesData)
|
||||||
|
if err != nil {
|
||||||
|
firstErr = errors.New("unable to set routes").Base(err)
|
||||||
|
if err == windows.ERROR_NOT_FOUND {
|
||||||
|
goto startOver
|
||||||
|
}
|
||||||
|
return firstErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if family == windows.AF_INET && address4 || family == windows.AF_INET6 && address6 {
|
||||||
|
err = t.luid.SetIPAddressesForFamily(family, addresses)
|
||||||
|
if err != nil {
|
||||||
|
firstErr = errors.New("unable to set ips").Base(err)
|
||||||
|
if err == windows.ERROR_NOT_FOUND {
|
||||||
|
goto startOver
|
||||||
|
}
|
||||||
|
return firstErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ipif, err := t.luid.IPInterface(family)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -136,56 +167,45 @@ func (t *WindowsTun) Start() error {
|
|||||||
ipif.DadTransmits = 0
|
ipif.DadTransmits = 0
|
||||||
ipif.ManagedAddressConfigurationSupported = false
|
ipif.ManagedAddressConfigurationSupported = false
|
||||||
ipif.OtherStatefulConfigurationSupported = false
|
ipif.OtherStatefulConfigurationSupported = false
|
||||||
ipif.NLMTU = t.options.MTU
|
if family == windows.AF_INET && (address4 || route4) || family == windows.AF_INET6 && (address6 || route6) {
|
||||||
ipif.UseAutomaticMetric = false
|
ipif.NLMTU = t.options.MTU
|
||||||
ipif.Metric = 0
|
}
|
||||||
|
if family == windows.AF_INET && route4 || family == windows.AF_INET6 && route6 {
|
||||||
|
ipif.UseAutomaticMetric = false
|
||||||
|
ipif.Metric = 0
|
||||||
|
}
|
||||||
err = ipif.Set()
|
err = ipif.Set()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
firstErr = errors.New("unable to set metric and MTU").Base(err)
|
||||||
|
if err == windows.ERROR_NOT_FOUND {
|
||||||
|
goto startOver
|
||||||
|
}
|
||||||
|
return firstErr
|
||||||
}
|
}
|
||||||
}
|
err = t.luid.SetDNS(family, dns, nil)
|
||||||
if has6 {
|
|
||||||
ipif, err := t.luid.IPInterface(windows.AF_INET6)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
firstErr = errors.New("unable to set DNS").Base(err)
|
||||||
}
|
if err == windows.ERROR_NOT_FOUND {
|
||||||
ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled
|
goto startOver
|
||||||
ipif.DadTransmits = 0
|
}
|
||||||
ipif.ManagedAddressConfigurationSupported = false
|
return firstErr
|
||||||
ipif.OtherStatefulConfigurationSupported = false
|
|
||||||
ipif.NLMTU = t.options.MTU
|
|
||||||
ipif.UseAutomaticMetric = false
|
|
||||||
ipif.Metric = 0
|
|
||||||
err = ipif.Set()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(t.options.DNS) > 0 {
|
|
||||||
dns := make([]netip.Addr, 0, len(t.options.DNS))
|
|
||||||
for _, ip := range t.options.DNS {
|
|
||||||
dns = append(dns, netip.MustParseAddr(ip))
|
|
||||||
}
|
|
||||||
err := t.luid.SetDNS(windows.AF_INET, dns, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = t.luid.SetDNS(windows.AF_INET6, dns, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if updater != nil {
|
if updater != nil {
|
||||||
t.changeCallback, err = winipcfg.RegisterInterfaceChangeCallback(func(notificationType winipcfg.MibNotificationType, iface *winipcfg.MibIPInterfaceRow) {
|
t.cbr, err = winipcfg.RegisterRouteChangeCallback(func(notificationType winipcfg.MibNotificationType, route *winipcfg.MibIPforwardRow2) {
|
||||||
|
updater.Update()
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.cbi, err = winipcfg.RegisterInterfaceChangeCallback(func(notificationType winipcfg.MibNotificationType, iface *winipcfg.MibIPInterfaceRow) {
|
||||||
updater.Update()
|
updater.Update()
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,12 +217,26 @@ func (t *WindowsTun) Close() error {
|
|||||||
}
|
}
|
||||||
t.closed = true
|
t.closed = true
|
||||||
|
|
||||||
if t.changeCallback != nil {
|
if t.cbr != nil {
|
||||||
t.changeCallback.Unregister()
|
t.cbr.Unregister()
|
||||||
|
}
|
||||||
|
if t.cbi != nil {
|
||||||
|
t.cbi.Unregister()
|
||||||
|
}
|
||||||
|
if t.luid != 0 {
|
||||||
|
t.luid.FlushRoutes(windows.AF_INET)
|
||||||
|
t.luid.FlushIPAddresses(windows.AF_INET)
|
||||||
|
t.luid.FlushDNS(windows.AF_INET)
|
||||||
|
t.luid.FlushRoutes(windows.AF_INET6)
|
||||||
|
t.luid.FlushIPAddresses(windows.AF_INET6)
|
||||||
|
t.luid.FlushDNS(windows.AF_INET6)
|
||||||
|
}
|
||||||
|
if t.session != (wintun.Session{}) {
|
||||||
|
t.session.End()
|
||||||
|
}
|
||||||
|
if t.adapter != nil {
|
||||||
|
t.adapter.Close()
|
||||||
}
|
}
|
||||||
t.session.End()
|
|
||||||
_ = t.adapter.Close()
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,75 +345,49 @@ func setinterface(network, address string, fd uintptr, iface *net.Interface) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func findOutboundInterface(tunIndex int, fixedName string) (*net.Interface, error) {
|
func findOutboundInterface(tunIndex int, fixedName string) (*net.Interface, error) {
|
||||||
interfaces, err := net.Interfaces()
|
if fixedName != "" {
|
||||||
|
return net.InterfaceByName(fixedName)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := winipcfg.GetIPForwardTable2(windows.AF_UNSPEC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
lowestMetric := ^uint32(0)
|
||||||
|
index := uint32(0)
|
||||||
|
lowestMetricWifi := ^uint32(0)
|
||||||
|
indexWifi := uint32(0)
|
||||||
|
for i := range r {
|
||||||
|
if r[i].DestinationPrefix.PrefixLength != 0 || r[i].InterfaceIndex == uint32(tunIndex) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ifrow, err := r[i].InterfaceLUID.Interface()
|
||||||
|
if err != nil || ifrow.OperStatus != winipcfg.IfOperStatusUp {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if fixedName != "" {
|
iface, err := r[i].InterfaceLUID.IPInterface(windows.AF_INET)
|
||||||
for _, iface := range interfaces {
|
if err != nil {
|
||||||
if iface.Index != tunIndex && iface.Name == fixedName {
|
iface, err = r[i].InterfaceLUID.IPInterface(windows.AF_INET6)
|
||||||
return &iface, nil
|
if err != nil {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var candidates []struct {
|
if ifrow.Type == windows.IF_TYPE_IEEE80211 {
|
||||||
index int
|
if r[i].Metric+iface.Metric < lowestMetricWifi {
|
||||||
score int
|
lowestMetricWifi = r[i].Metric + iface.Metric
|
||||||
|
indexWifi = r[i].InterfaceIndex
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if r[i].Metric+iface.Metric < lowestMetric {
|
||||||
|
lowestMetric = r[i].Metric + iface.Metric
|
||||||
|
index = r[i].InterfaceIndex
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for i, iface := range interfaces {
|
if indexWifi != 0 {
|
||||||
if iface.Index == tunIndex {
|
index = indexWifi
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.Contains(iface.Name, "vEthernet") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if iface.Flags&net.FlagUp == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if iface.Flags&net.FlagLoopback != 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
addrs, err := iface.Addrs()
|
|
||||||
if err != nil || len(addrs) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
candidates = append(candidates, struct {
|
|
||||||
index int
|
|
||||||
score int
|
|
||||||
}{i, scoreWindowsInterface(&iface, addrs)})
|
|
||||||
}
|
}
|
||||||
|
return net.InterfaceByIndex(int(index))
|
||||||
sort.Slice(candidates, func(i, j int) bool {
|
|
||||||
if candidates[i].score != candidates[j].score {
|
|
||||||
return candidates[i].score > candidates[j].score
|
|
||||||
}
|
|
||||||
return interfaces[candidates[i].index].Name < interfaces[candidates[j].index].Name
|
|
||||||
})
|
|
||||||
if len(candidates) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
iface := interfaces[candidates[0].index]
|
|
||||||
return &iface, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func scoreWindowsInterface(iface *net.Interface, addrs []net.Addr) int {
|
|
||||||
score := 0
|
|
||||||
|
|
||||||
name := strings.ToLower(iface.Name)
|
|
||||||
if strings.Contains(name, "wlan") || strings.Contains(name, "wi-fi") {
|
|
||||||
score += 2
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, addr := range addrs {
|
|
||||||
if strings.HasPrefix(addr.String(), "192.168.") {
|
|
||||||
score++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return score
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user