From 0a21d5215c448f45bde059f0913186f0c5854f76 Mon Sep 17 00:00:00 2001 From: Mark Pashmfouroush Date: Mon, 20 May 2024 13:17:02 +0100 Subject: [PATCH] wireguard: add generic wireguard support Signed-off-by: Mark Pashmfouroush --- app/app.go | 97 ++++++++++++++++++++++++++++++++++++++++----- example_config.json | 3 +- iputils/iputils.go | 84 ++++++++++++++++++++++----------------- main.go | 16 ++++---- 4 files changed, 145 insertions(+), 55 deletions(-) diff --git a/app/app.go b/app/app.go index 66982fa28..766a165bf 100644 --- a/app/app.go +++ b/app/app.go @@ -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" @@ -19,16 +20,17 @@ 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 { @@ -36,6 +38,14 @@ type PsiphonOptions struct { } 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") } @@ -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")) diff --git a/example_config.json b/example_config.json index e6a737544..76830149d 100644 --- a/example_config.json +++ b/example_config.json @@ -11,5 +11,6 @@ "rtt": "1000ms", "cache-dir": "", "tun-experimental": false, - "fwmark": "0x1375" + "fwmark": "0x1375", + "wgconf": "" } diff --git a/iputils/iputils.go b/iputils/iputils.go index 6633881d1..98b863e0a 100644 --- a/iputils/iputils.go +++ b/iputils/iputils.go @@ -5,7 +5,9 @@ import ( "fmt" "math/big" "math/rand" + "net" "net/netip" + "strconv" "time" ) @@ -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") +} diff --git a/main.go b/main.go index 1ab25bb7b..b2bb56413 100644 --- a/main.go +++ b/main.go @@ -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") ) @@ -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 {