Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wireguard: add generic wireguard support #108

Merged
merged 1 commit into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 87 additions & 10 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net/netip"
"path"

"github.com/bepass-org/warp-plus/iputils"
"github.com/bepass-org/warp-plus/psiphon"
"github.com/bepass-org/warp-plus/warp"
"github.com/bepass-org/warp-plus/wiresocks"
Expand All @@ -19,23 +20,32 @@ const doubleMTU = 1280 // minimum mtu for IPv6, may cause frag reassembly somewh
const connTestEndpoint = "http://1.1.1.1:80/"

type WarpOptions struct {
Bind netip.AddrPort
Endpoint string
License string
DnsAddr netip.Addr
Psiphon *PsiphonOptions
Gool bool
Scan *wiresocks.ScanOptions
CacheDir string
Tun bool
FwMark uint32
Bind netip.AddrPort
Endpoint string
License string
DnsAddr netip.Addr
Psiphon *PsiphonOptions
Gool bool
Scan *wiresocks.ScanOptions
CacheDir string
Tun bool
FwMark uint32
WireguardConfig string
}

type PsiphonOptions struct {
Country string
}

func RunWarp(ctx context.Context, l *slog.Logger, opts WarpOptions) error {
if opts.WireguardConfig != "" {
if err := runWireguard(ctx, l, opts); err != nil {
return err
}

return nil
}

if opts.Psiphon != nil && opts.Gool {
return errors.New("can't use psiphon and gool at the same time")
}
Expand Down Expand Up @@ -101,6 +111,73 @@ func RunWarp(ctx context.Context, l *slog.Logger, opts WarpOptions) error {
return warpErr
}

func runWireguard(ctx context.Context, l *slog.Logger, opts WarpOptions) error {
conf, err := wiresocks.ParseConfig(opts.WireguardConfig)
if err != nil {
return err
}

// Set up MTU
conf.Interface.MTU = singleMTU
// Set up DNS Address
conf.Interface.DNS = []netip.Addr{opts.DnsAddr}

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

// Try resolving if the endpoint is a domain
addr, err := iputils.ParseResolveAddressPort(peer.Endpoint, false)
if err == nil {
peer.Endpoint = addr.String()
}

conf.Peers[i] = peer
}

if opts.Tun {
// Create a new tun interface
tunDev, err := newNormalTun([]netip.Addr{opts.DnsAddr})
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
}

// 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 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"))
Expand Down
3 changes: 2 additions & 1 deletion example_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
"rtt": "1000ms",
"cache-dir": "",
"tun-experimental": false,
"fwmark": "0x1375"
"fwmark": "0x1375",
"wgconf": ""
}
84 changes: 47 additions & 37 deletions iputils/iputils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (
"fmt"
"math/big"
"math/rand"
"net"
"net/netip"
"strconv"
"time"
)

Expand Down Expand Up @@ -55,40 +57,48 @@ func RandomIPFromPrefix(cidr netip.Prefix) (netip.Addr, error) {
return randomAddress.Unmap(), nil
}

// func ParseResolveAddressPort(hostname string) (netip.AddrPort, error) {
// // Attempt to split the hostname into a host and port
// host, port, err := net.SplitHostPort(hostname)
// if err != nil {
// return netip.AddrPort{}, fmt.Errorf("can't parse provided hostname into host and port: %w", err)
// }

// // Convert the string port to a uint16
// portInt, err := strconv.Atoi(port)
// if err != nil {
// return netip.AddrPort{}, fmt.Errorf("error parsing port: %w", err)
// }

// if portInt < 1 || portInt > 65535 {
// return netip.AddrPort{}, fmt.Errorf("port number %d is out of range", portInt)
// }

// // Attempt to parse the host into an IP. Return on success.
// addr, err := netip.ParseAddr(host)
// if err == nil {
// return netip.AddrPortFrom(addr.Unmap(), uint16(portInt)), nil
// }

// // If the host wasn't an IP, perform a lookup
// ips, err := net.LookupIP(host)
// if err != nil {
// return netip.AddrPort{}, fmt.Errorf("hostname lookup failed: %w", err)
// }

// // Take the first IP and then return it
// addr, ok := netip.AddrFromSlice(ips[0])
// if !ok {
// return netip.AddrPort{}, errors.New("failed to parse ip")
// }

// return netip.AddrPortFrom(addr.Unmap(), uint16(portInt)), nil
// }
func ParseResolveAddressPort(hostname string, includev6 bool) (netip.AddrPort, error) {
// Attempt to split the hostname into a host and port
host, port, err := net.SplitHostPort(hostname)
if err != nil {
return netip.AddrPort{}, fmt.Errorf("can't parse provided hostname into host and port: %w", err)
}

// Convert the string port to a uint16
portInt, err := strconv.Atoi(port)
if err != nil {
return netip.AddrPort{}, fmt.Errorf("error parsing port: %w", err)
}

if portInt < 1 || portInt > 65535 {
return netip.AddrPort{}, fmt.Errorf("port number %d is out of range", portInt)
}

// Attempt to parse the host into an IP. Return on success.
addr, err := netip.ParseAddr(host)
if err == nil {
return netip.AddrPortFrom(addr.Unmap(), uint16(portInt)), nil
}

// If the host wasn't an IP, perform a lookup
ips, err := net.LookupIP(host)
if err != nil {
return netip.AddrPort{}, fmt.Errorf("hostname lookup failed: %w", err)
}

for _, ip := range ips {
// Take the first IP and then return it
addr, ok := netip.AddrFromSlice(ip)
if !ok {
continue
}

if addr.Unmap().Is4() {
return netip.AddrPortFrom(addr.Unmap(), uint16(portInt)), nil
} else if includev6 {
return netip.AddrPortFrom(addr.Unmap(), uint16(portInt)), nil
}
}

return netip.AddrPort{}, errors.New("no valid IP addresses found")
}
16 changes: 9 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func main() {
cacheDir = fs.StringLong("cache-dir", "", "directory to store generated profiles")
tun = fs.BoolLong("tun-experimental", "enable tun interface (experimental)")
fwmark = fs.UintLong("fwmark", 0x1375, "set linux firewall mark for tun mode")
wgConf = fs.StringLong("wgconf", "", "path to a normal wireguard config")
_ = fs.String('c', "config", "", "path to config file")
verFlag = fs.BoolLong("version", "displays version number")
)
Expand Down Expand Up @@ -138,13 +139,14 @@ func main() {
}

opts := app.WarpOptions{
Bind: bindAddrPort,
Endpoint: *endpoint,
License: *key,
DnsAddr: dnsAddr,
Gool: *gool,
Tun: *tun,
FwMark: uint32(*fwmark),
Bind: bindAddrPort,
Endpoint: *endpoint,
License: *key,
DnsAddr: dnsAddr,
Gool: *gool,
Tun: *tun,
FwMark: uint32(*fwmark),
WireguardConfig: *wgConf,
}

switch {
Expand Down