mirror of
https://github.com/amnezia-vpn/euphoria-windows.git
synced 2026-05-17 08:15:59 +03:00
157 lines
4.1 KiB
Go
157 lines
4.1 KiB
Go
/* SPDX-License-Identifier: MIT
|
|
*
|
|
* Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
|
|
*/
|
|
|
|
package tunnel
|
|
|
|
import (
|
|
"log"
|
|
"sync"
|
|
"time"
|
|
|
|
"golang.org/x/sys/windows"
|
|
|
|
"github.com/amnezia-vpn/euphoria-windows/tunnel/winipcfg"
|
|
"github.com/amnezia-vpn/euphoria/conn"
|
|
"github.com/amnezia-vpn/euphoria/tun"
|
|
)
|
|
|
|
func bindSocketRoute(family winipcfg.AddressFamily, binder conn.BindSocketToInterface, ourLUID winipcfg.LUID, lastLUID *winipcfg.LUID, lastIndex *uint32, blackholeWhenLoop bool) error {
|
|
r, err := winipcfg.GetIPForwardTable2(family)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
lowestMetric := ^uint32(0)
|
|
index := uint32(0) // Zero is "unspecified", which for IP_UNICAST_IF resets the value, which is what we want.
|
|
luid := winipcfg.LUID(0) // Hopefully luid zero is unspecified, but hard to find docs saying so.
|
|
for i := range r {
|
|
if r[i].DestinationPrefix.PrefixLength != 0 || r[i].InterfaceLUID == ourLUID {
|
|
continue
|
|
}
|
|
ifrow, err := r[i].InterfaceLUID.Interface()
|
|
if err != nil || ifrow.OperStatus != winipcfg.IfOperStatusUp {
|
|
continue
|
|
}
|
|
|
|
iface, err := r[i].InterfaceLUID.IPInterface(family)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
if r[i].Metric+iface.Metric < lowestMetric {
|
|
lowestMetric = r[i].Metric + iface.Metric
|
|
index = r[i].InterfaceIndex
|
|
luid = r[i].InterfaceLUID
|
|
}
|
|
}
|
|
if luid == *lastLUID && index == *lastIndex {
|
|
return nil
|
|
}
|
|
*lastLUID = luid
|
|
*lastIndex = index
|
|
blackhole := blackholeWhenLoop && index == 0
|
|
if family == windows.AF_INET {
|
|
log.Printf("Binding v4 socket to interface %d (blackhole=%v)", index, blackhole)
|
|
return binder.BindSocketToInterface4(index, blackhole)
|
|
} else if family == windows.AF_INET6 {
|
|
log.Printf("Binding v6 socket to interface %d (blackhole=%v)", index, blackhole)
|
|
return binder.BindSocketToInterface6(index, blackhole)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func monitorDefaultRoutes(family winipcfg.AddressFamily, binder conn.BindSocketToInterface, autoMTU bool, blackholeWhenLoop bool, tun *tun.NativeTun) ([]winipcfg.ChangeCallback, error) {
|
|
var minMTU uint32
|
|
if family == windows.AF_INET {
|
|
minMTU = 576
|
|
} else if family == windows.AF_INET6 {
|
|
minMTU = 1280
|
|
}
|
|
ourLUID := winipcfg.LUID(tun.LUID())
|
|
lastLUID := winipcfg.LUID(0)
|
|
lastIndex := ^uint32(0)
|
|
lastMTU := uint32(0)
|
|
doIt := func() error {
|
|
err := bindSocketRoute(family, binder, ourLUID, &lastLUID, &lastIndex, blackholeWhenLoop)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !autoMTU {
|
|
return nil
|
|
}
|
|
mtu := uint32(0)
|
|
if lastLUID != 0 {
|
|
iface, err := lastLUID.Interface()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if iface.MTU > 0 {
|
|
mtu = iface.MTU
|
|
}
|
|
}
|
|
if mtu > 0 && lastMTU != mtu {
|
|
iface, err := ourLUID.IPInterface(family)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
iface.NLMTU = mtu - 80
|
|
if iface.NLMTU < minMTU {
|
|
iface.NLMTU = minMTU
|
|
}
|
|
err = iface.Set()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tun.ForceMTU(int(iface.NLMTU)) // TODO: having one MTU for both v4 and v6 kind of breaks the windows model, so right now this just gets the second one which is... bad.
|
|
lastMTU = mtu
|
|
}
|
|
return nil
|
|
}
|
|
err := doIt()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
firstBurst := time.Time{}
|
|
burstMutex := sync.Mutex{}
|
|
burstTimer := time.AfterFunc(time.Hour*200, func() {
|
|
burstMutex.Lock()
|
|
firstBurst = time.Time{}
|
|
doIt()
|
|
burstMutex.Unlock()
|
|
})
|
|
burstTimer.Stop()
|
|
bump := func() {
|
|
burstMutex.Lock()
|
|
burstTimer.Reset(time.Millisecond * 150)
|
|
if firstBurst.IsZero() {
|
|
firstBurst = time.Now()
|
|
} else if time.Since(firstBurst) > time.Second*2 {
|
|
firstBurst = time.Time{}
|
|
burstTimer.Stop()
|
|
doIt()
|
|
}
|
|
burstMutex.Unlock()
|
|
}
|
|
|
|
cbr, err := winipcfg.RegisterRouteChangeCallback(func(notificationType winipcfg.MibNotificationType, route *winipcfg.MibIPforwardRow2) {
|
|
if route != nil && route.DestinationPrefix.PrefixLength == 0 {
|
|
bump()
|
|
}
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cbi, err := winipcfg.RegisterInterfaceChangeCallback(func(notificationType winipcfg.MibNotificationType, iface *winipcfg.MibIPInterfaceRow) {
|
|
if notificationType == winipcfg.MibParameterNotification {
|
|
bump()
|
|
}
|
|
})
|
|
if err != nil {
|
|
cbr.Unregister()
|
|
return nil, err
|
|
}
|
|
return []winipcfg.ChangeCallback{cbr, cbi}, nil
|
|
}
|