Skip to content

Commit

Permalink
tun: windows support (experimental)
Browse files Browse the repository at this point in the history
Signed-off-by: Mark Pashmfouroush <[email protected]>
  • Loading branch information
markpash committed May 13, 2024
1 parent a6ada4e commit 8458319
Show file tree
Hide file tree
Showing 8 changed files with 220 additions and 40 deletions.
30 changes: 14 additions & 16 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,14 @@ type WarpOptions struct {
Gool bool
Scan *wiresocks.ScanOptions
CacheDir string
Tun *TunOptions
Tun bool
FwMark uint32
}

type PsiphonOptions struct {
Country string
}

type TunOptions struct {
FwMark uint32
}

func RunWarp(ctx context.Context, l *slog.Logger, opts WarpOptions) error {
if opts.Psiphon != nil && opts.Gool {
return errors.New("can't use psiphon and gool at the same time")
Expand All @@ -46,7 +43,7 @@ func RunWarp(ctx context.Context, l *slog.Logger, opts WarpOptions) error {
return errors.New("must provide country for psiphon")
}

if opts.Psiphon != nil && opts.Tun != nil {
if opts.Psiphon != nil && opts.Tun {
return errors.New("can't use psiphon and tun at the same time")
}

Expand Down Expand Up @@ -121,15 +118,15 @@ func runWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoint str
conf.Peers[i] = peer
}

if opts.Tun != nil {
if opts.Tun {
// Create a new tun interface
tunDev, err := newNormalTun()
if err != nil {
return err
}

// Establish wireguard tunnel on tun interface
if err := establishWireguard(l, conf, tunDev, opts.Tun.FwMark); err != nil {
if err := establishWireguard(l, conf, tunDev, true, opts.FwMark); err != nil {
return err
}
l.Info("serving tun", "interface", "warp0")
Expand All @@ -143,7 +140,7 @@ func runWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoint str
}

// Establish wireguard on userspace stack
if err := establishWireguard(l, conf, tunDev, 0); err != nil {
if err := establishWireguard(l, conf, tunDev, false, opts.FwMark); err != nil {
return err
}

Expand Down Expand Up @@ -186,8 +183,8 @@ func runWarpInWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoi
return err
}

// Establish wireguard on userspace stack
if err := establishWireguard(l.With("gool", "outer"), conf, tunDev, 0); err != nil {
// Establish wireguard on userspace stack and bind the wireguard sockets to the default interface and apply
if err := establishWireguard(l.With("gool", "outer"), conf, tunDev, opts.Tun, opts.FwMark); err != nil {
return err
}

Expand Down Expand Up @@ -218,15 +215,16 @@ func runWarpInWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoi
conf.Peers[i] = peer
}

if opts.Tun != nil {
if opts.Tun {
// Create a new tun interface
tunDev, err := newNormalTun()
if err != nil {
return err
}

// Establish wireguard tunnel on tun interface
if err := establishWireguard(l.With("gool", "inner"), conf, tunDev, opts.Tun.FwMark); err != nil {
// Establish wireguard tunnel on tun interface but don't bind
// wireguard sockets to default interface and don't apply fwmark.
if err := establishWireguard(l.With("gool", "inner"), conf, tunDev, false, opts.FwMark); err != nil {
return err
}
l.Info("serving tun", "interface", "warp0")
Expand All @@ -240,7 +238,7 @@ func runWarpInWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoi
}

// Establish wireguard on userspace stack
if err := establishWireguard(l.With("gool", "inner"), conf, tunDev, 0); err != nil {
if err := establishWireguard(l.With("gool", "inner"), conf, tunDev, false, opts.FwMark); err != nil {
return err
}

Expand Down Expand Up @@ -283,7 +281,7 @@ func runWarpWithPsiphon(ctx context.Context, l *slog.Logger, opts WarpOptions, e
}

// Establish wireguard on userspace stack
if err := establishWireguard(l, conf, tunDev, 0); err != nil {
if err := establishWireguard(l, conf, tunDev, false, opts.FwMark); err != nil {
return err
}

Expand Down
21 changes: 21 additions & 0 deletions app/tun_others.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//go:build !windows

package app

import (
"github.com/bepass-org/warp-plus/wireguard/device"
wgtun "github.com/bepass-org/warp-plus/wireguard/tun"
)

func newNormalTun() (wgtun.Device, error) {
tunDev, err := wgtun.CreateTUN("warp0", 1280)
if err != nil {
return nil, err
}
return tunDev, nil

}

func bindToIface(_ *device.Device) error {
return nil
}
164 changes: 164 additions & 0 deletions app/tun_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package app

import (
"errors"
"fmt"
"net"
"net/netip"

"github.com/bepass-org/warp-plus/wireguard/conn"
"github.com/bepass-org/warp-plus/wireguard/device"
"github.com/bepass-org/warp-plus/wireguard/tun"
wgtun "github.com/bepass-org/warp-plus/wireguard/tun"
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
)

const wintunGUID = "c33d325f-20cd-44e5-998c-19b0c15b4df1"
const family4 = winipcfg.AddressFamily(windows.AF_INET)
const family6 = winipcfg.AddressFamily(windows.AF_INET6)

func newNormalTun() (wgtun.Device, error) {
guid, _ := windows.GUIDFromString(wintunGUID)
tunDev, err := wgtun.CreateTUNWithRequestedGUID("warp0", &guid, 1280)
if err != nil {
return nil, err
}

nativeTunDevice := tunDev.(*tun.NativeTun)
luid := winipcfg.LUID(nativeTunDevice.LUID())

err = luid.SetIPAddressesForFamily(family4, []netip.Prefix{netip.MustParsePrefix("172.16.0.2/24")})
if err != nil {
return nil, err
}

// Set this to break IPv6 and prevent leaks. TODO: fix windows ipv6 tun
err = luid.SetIPAddressesForFamily(family6, []netip.Prefix{netip.MustParsePrefix("fd12:3456:789a:1::1/128")})
if err != nil {
return nil, err
}

tryAgain4:
err = luid.SetRoutesForFamily(family4, []*winipcfg.RouteData{{Destination: netip.MustParsePrefix("0.0.0.0/0"), NextHop: netip.IPv4Unspecified(), Metric: 0}})
if err != nil && err == windows.ERROR_NOT_FOUND {
goto tryAgain4
} else if err != nil {
return nil, err
}

var ipif *winipcfg.MibIPInterfaceRow
ipif, err = luid.IPInterface(family4)
if err != nil {
return nil, err
}
ipif.ForwardingEnabled = true
ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled
ipif.DadTransmits = 0
ipif.ManagedAddressConfigurationSupported = false
ipif.OtherStatefulConfigurationSupported = false
ipif.NLMTU = uint32(1280)
ipif.UseAutomaticMetric = false
ipif.Metric = 0

err = ipif.Set()
if err != nil && err == windows.ERROR_NOT_FOUND {
goto tryAgain4
} else if err != nil {
return nil, fmt.Errorf("unable to set metric and MTU: %w", err)
}

tryAgain6:
err = luid.SetRoutesForFamily(family6, []*winipcfg.RouteData{{Destination: netip.MustParsePrefix("::/0"), NextHop: netip.IPv6Unspecified(), Metric: 0}})
if err != nil && err == windows.ERROR_NOT_FOUND {
goto tryAgain6
} else if err != nil {
return nil, err
}

var ipif6 *winipcfg.MibIPInterfaceRow
ipif6, err = luid.IPInterface(family6)
if err != nil {
return nil, err
}
ipif6.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled
ipif6.DadTransmits = 0
ipif6.ManagedAddressConfigurationSupported = false
ipif6.OtherStatefulConfigurationSupported = false
ipif6.NLMTU = uint32(1280)
ipif6.UseAutomaticMetric = false
ipif6.Metric = 0

err = ipif6.Set()
if err != nil && err == windows.ERROR_NOT_FOUND {
goto tryAgain6
} else if err != nil {
return nil, fmt.Errorf("unable to set metric and MTU: %w", err)
}

return tunDev, nil

}

func getAutoDetectInterfaceByFamily(family winipcfg.AddressFamily) (string, error) {
interfaces, err := winipcfg.GetAdaptersAddresses(family, winipcfg.GAAFlagIncludeGateways)
if err != nil {
return "", fmt.Errorf("get default interface failure. %w", err)
}

var destination netip.Prefix
if family == family4 {
destination = netip.PrefixFrom(netip.IPv4Unspecified(), 0)
} else {
destination = netip.PrefixFrom(netip.IPv6Unspecified(), 0)
}

for _, ifaceM := range interfaces {
if ifaceM.OperStatus != winipcfg.IfOperStatusUp {
continue
}

ifname := ifaceM.FriendlyName()

if ifname == "warp0" {
continue
}

for gatewayAddress := ifaceM.FirstGatewayAddress; gatewayAddress != nil; gatewayAddress = gatewayAddress.Next {
nextHop, _ := netip.AddrFromSlice(gatewayAddress.Address.IP())

if _, err = ifaceM.LUID.Route(destination, nextHop.Unmap()); err == nil {
return ifname, nil
}
}
}

return "", errors.New("interface not found")
}

func bindToIface(dev *device.Device) error {
ifaceName, err := getAutoDetectInterfaceByFamily(winipcfg.AddressFamily(family4))
if err != nil {
return err
}

iface, err := net.InterfaceByName(ifaceName)
if err != nil {
return err
}

bind, ok := dev.Bind().(conn.BindSocketToInterface)
if !ok {
return errors.New("failed to cast to bindsockettointerface")
}

if err := bind.BindSocketToInterface4(uint32(iface.Index), false); err != nil {
return err
}

if err := bind.BindSocketToInterface6(uint32(iface.Index), false); err != nil {
return err
}

return nil
}
17 changes: 7 additions & 10 deletions app/wg.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,6 @@ import (
"github.com/bepass-org/warp-plus/wiresocks"
)

func newNormalTun() (wgtun.Device, error) {
tunDev, err := wgtun.CreateTUN("warp0", 1280)
if err != nil {
return nil, err
}

return tunDev, nil
}

func newUsermodeTun(conf *wiresocks.Configuration) (wgtun.Device, *netstack.Net, error) {
tunDev, tnet, err := netstack.CreateNetTUN(conf.Interface.Addresses, conf.Interface.DNS, conf.Interface.MTU)
if err != nil {
Expand Down Expand Up @@ -68,7 +59,7 @@ func usermodeTunTest(ctx context.Context, l *slog.Logger, tnet *netstack.Net) er
return nil
}

func establishWireguard(l *slog.Logger, conf *wiresocks.Configuration, tunDev wgtun.Device, fwmark uint32) error {
func establishWireguard(l *slog.Logger, conf *wiresocks.Configuration, tunDev wgtun.Device, bind bool, fwmark uint32) error {
// create the IPC message to establish the wireguard conn
var request bytes.Buffer

Expand Down Expand Up @@ -103,5 +94,11 @@ func establishWireguard(l *slog.Logger, conf *wiresocks.Configuration, tunDev wg
return err
}

if bind {
if err := bindToIface(dev); err != nil {
return err
}
}

return nil
}
2 changes: 1 addition & 1 deletion example_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
"scan": true,
"rtt": "1000ms",
"cache-dir": "",
"tun": false,
"tun-experimental": false,
"fwmark": "0x1375"
}
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ require (
golang.org/x/net v0.24.0
golang.org/x/sys v0.19.0
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
golang.zx2c4.com/wireguard/windows v0.5.3
gvisor.dev/gvisor v0.0.0-20240503213918-b7c924bc64f8
)

Expand Down Expand Up @@ -55,6 +56,7 @@ require (
github.com/libp2p/go-reuseport v0.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect
github.com/miekg/dns v1.1.44-0.20210804161652-ab67aa642300 // indirect
github.com/mroth/weightedrand v1.0.0 // indirect
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
Expand All @@ -79,7 +81,6 @@ require (
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/goptlib v1.5.0 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
golang.org/x/exp/typeparams v0.0.0-20240119083558-1b970713d09a // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
Expand Down
Loading

0 comments on commit 8458319

Please sign in to comment.