Skip to content

Commit

Permalink
tun: add tun support (experimental) (#79)
Browse files Browse the repository at this point in the history
* tun: add tun support (linux)
* tun: windows support (experimental)

Signed-off-by: Mark Pashmfouroush <[email protected]>
  • Loading branch information
markpash authored May 13, 2024
1 parent 9123492 commit 3a9504a
Show file tree
Hide file tree
Showing 11 changed files with 452 additions and 104 deletions.
158 changes: 127 additions & 31 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

const singleMTU = 1330
const doubleMTU = 1280 // minimum mtu for IPv6, may cause frag reassembly somewhere
const connTestEndpoint = "http://1.1.1.1:80/"

type WarpOptions struct {
Bind netip.AddrPort
Expand All @@ -25,6 +26,8 @@ type WarpOptions struct {
Gool bool
Scan *wiresocks.ScanOptions
CacheDir string
Tun bool
FwMark uint32
}

type PsiphonOptions struct {
Expand All @@ -40,6 +43,10 @@ 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 {
return errors.New("can't use psiphon and tun at the same time")
}

// create identities
if err := createPrimaryAndSecondaryIdentities(l.With("subsystem", "warp/account"), opts); err != nil {
return err
Expand Down Expand Up @@ -94,116 +101,205 @@ func RunWarp(ctx context.Context, l *slog.Logger, opts WarpOptions) error {
}

func runWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoint string) error {
// Set up primary/outer warp config
conf, err := wiresocks.ParseConfig(path.Join(opts.CacheDir, "primary", "wgcf-profile.ini"))
if err != nil {
return err
}

// Set up MTU
conf.Interface.MTU = singleMTU

// Enable trick and keepalive on all peers in config
for i, peer := range conf.Peers {
peer.Endpoint = endpoint
peer.Trick = true
peer.KeepAlive = 3
conf.Peers[i] = peer
}

tnet, err := wiresocks.StartWireguard(ctx, l, conf)
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, true, opts.FwMark); err != nil {
return err
}
l.Info("serving tun", "interface", "warp0")
return nil
}

// Create userspace tun network stack
tunDev, tnet, err := newUsermodeTun(conf)
if err != nil {
return err
}

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

// Test wireguard connectivity
if err := usermodeTunTest(ctx, l, tnet); err != nil {
return err
}

// Run a proxy on the userspace stack
_, err = wiresocks.StartProxy(ctx, l, tnet, opts.Bind)
if err != nil {
return err
}

l.Info("serving proxy", "address", opts.Bind)

return nil
}

func runWarpWithPsiphon(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoint string) error {
func runWarpInWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoints []string) error {
// Set up primary/outer warp config
conf, err := wiresocks.ParseConfig(path.Join(opts.CacheDir, "primary", "wgcf-profile.ini"))
if err != nil {
return err
}

// Set up MTU
conf.Interface.MTU = singleMTU

// Enable trick and keepalive on all peers in config
for i, peer := range conf.Peers {
peer.Endpoint = endpoint
peer.Endpoint = endpoints[0]
peer.Trick = true
peer.KeepAlive = 3
conf.Peers[i] = peer
}

tnet, err := wiresocks.StartWireguard(ctx, l, conf)
// Create userspace tun network stack
tunDev, tnet, err := newUsermodeTun(conf)
if err != nil {
return err
}

warpBind, err := tnet.StartProxy(netip.MustParseAddrPort("127.0.0.1:0"))
// 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
}

// Test wireguard connectivity
if err := usermodeTunTest(ctx, l, tnet); err != nil {
return err
}

// Create a UDP port forward between localhost and the remote endpoint
addr, err := wiresocks.NewVtunUDPForwarder(ctx, netip.MustParseAddrPort("127.0.0.1:0"), endpoints[0], tnet, singleMTU)
if err != nil {
return err
}

// run psiphon
err = psiphon.RunPsiphon(ctx, l.With("subsystem", "psiphon"), warpBind.String(), opts.CacheDir, opts.Bind.String(), opts.Psiphon.Country)
// Set up secondary/inner warp config
conf, err = wiresocks.ParseConfig(path.Join(opts.CacheDir, "secondary", "wgcf-profile.ini"))
if err != nil {
return fmt.Errorf("unable to run psiphon %w", err)
return err
}

l.Info("serving proxy", "address", opts.Bind)
// Set up MTU
conf.Interface.MTU = doubleMTU

// Enable keepalive on all peers in config
for i, peer := range conf.Peers {
peer.Endpoint = addr.String()
peer.KeepAlive = 10
conf.Peers[i] = peer
}

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

// 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")
return nil
}

// Create userspace tun network stack
tunDev, tnet, err = newUsermodeTun(conf)
if err != nil {
return err
}

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

// Test wireguard connectivity
if err := usermodeTunTest(ctx, l, tnet); err != nil {
return err
}

_, err = wiresocks.StartProxy(ctx, l, tnet, opts.Bind)
if err != nil {
return err
}

l.Info("serving proxy", "address", opts.Bind)
return nil
}

func runWarpInWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoints []string) error {
// Run outer warp
func runWarpWithPsiphon(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoint string) error {
// Set up primary/outer warp config
conf, err := wiresocks.ParseConfig(path.Join(opts.CacheDir, "primary", "wgcf-profile.ini"))
if err != nil {
return err
}

// Set up MTU
conf.Interface.MTU = singleMTU

// Enable trick and keepalive on all peers in config
for i, peer := range conf.Peers {
peer.Endpoint = endpoints[0]
peer.Endpoint = endpoint
peer.Trick = true
peer.KeepAlive = 3
conf.Peers[i] = peer
}

tnet, err := wiresocks.StartWireguard(ctx, l.With("gool", "outer"), conf)
// Create userspace tun network stack
tunDev, tnet, err := newUsermodeTun(conf)
if err != nil {
return err
}

// Create a UDP port forward between localhost and the remote endpoint
addr, err := wiresocks.NewVtunUDPForwarder(ctx, netip.MustParseAddrPort("127.0.0.1:0"), endpoints[1], tnet, singleMTU)
if err != nil {
// Establish wireguard on userspace stack
if err := establishWireguard(l, conf, tunDev, false, opts.FwMark); err != nil {
return err
}

// Run inner warp
conf, err = wiresocks.ParseConfig(path.Join(opts.CacheDir, "secondary", "wgcf-profile.ini"))
if err != nil {
// Test wireguard connectivity
if err := usermodeTunTest(ctx, l, tnet); err != nil {
return err
}
conf.Interface.MTU = doubleMTU

for i, peer := range conf.Peers {
peer.Endpoint = addr.String()
peer.KeepAlive = 10
conf.Peers[i] = peer
}

tnet, err = wiresocks.StartWireguard(ctx, l.With("gool", "inner"), conf)
// Run a proxy on the userspace stack
warpBind, err := wiresocks.StartProxy(ctx, l, tnet, netip.MustParseAddrPort("127.0.0.1:0"))
if err != nil {
return err
}

_, err = tnet.StartProxy(opts.Bind)
// run psiphon
err = psiphon.RunPsiphon(ctx, l.With("subsystem", "psiphon"), warpBind.String(), opts.CacheDir, opts.Bind.String(), opts.Psiphon.Country)
if err != nil {
return err
return fmt.Errorf("unable to run psiphon %w", err)
}

l.Info("serving proxy", "address", opts.Bind)
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
}
Loading

0 comments on commit 3a9504a

Please sign in to comment.