diff --git a/app/app.go b/app/app.go index dea951783..4fe5b1c5b 100644 --- a/app/app.go +++ b/app/app.go @@ -12,6 +12,8 @@ import ( "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/wireguard/tun" + "github.com/bepass-org/warp-plus/wireguard/tun/netstack" "github.com/bepass-org/warp-plus/wiresocks" ) @@ -80,7 +82,7 @@ func RunWarp(ctx context.Context, l *slog.Logger, opts WarpOptions) error { return err } - l.Info("scan results", "endpoints", res) + l.Debug("scan results", "endpoints", res) endpoints = make([]string, len(res)) for i := 0; i < len(res); i++ { @@ -122,7 +124,7 @@ func runWireguard(ctx context.Context, l *slog.Logger, opts WarpOptions) error { // Enable trick and keepalive on all peers in config for i, peer := range conf.Peers { peer.Trick = true - peer.KeepAlive = 3 + peer.KeepAlive = 5 // Try resolving if the endpoint is a domain addr, err := iputils.ParseResolveAddressPort(peer.Endpoint, false, opts.DnsAddr.String()) @@ -134,35 +136,56 @@ func runWireguard(ctx context.Context, l *slog.Logger, opts WarpOptions) error { } 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 + var werr error + var tunDev tun.Device + for _, t := range []string{"t1", "t2"} { + // Create a new tun interface + tunDev, werr = newNormalTun([]netip.Addr{opts.DnsAddr}) + if werr != nil { + continue + } + + werr = establishWireguard(l, conf, tunDev, true, opts.FwMark, t) + if werr != nil { + continue + } + break } + if werr != nil { + return werr + } + 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 - } + var werr error + var tnet *netstack.Net + var tunDev tun.Device + for _, t := range []string{"t1", "t2"} { + // Create userspace tun network stack + tunDev, tnet, werr = netstack.CreateNetTUN(conf.Interface.Addresses, conf.Interface.DNS, conf.Interface.MTU) + if err != nil { + continue + } + + werr = establishWireguard(l, conf, tunDev, false, opts.FwMark, t) + if werr != nil { + continue + } - // // Test wireguard connectivity - // if err := usermodeTunTest(ctx, l, tnet); err != nil { - // return err - // } + // Test wireguard connectivity + werr = usermodeTunTest(ctx, l, tnet) + if werr != nil { + continue + } + break + } + if werr != nil { + return werr + } // Run a proxy on the userspace stack _, err = wiresocks.StartProxy(ctx, l, tnet, opts.Bind) @@ -194,7 +217,7 @@ func runWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoint str for i, peer := range conf.Peers { peer.Endpoint = endpoint peer.Trick = true - peer.KeepAlive = 3 + peer.KeepAlive = 5 if opts.Reserved != "" { r, err := wiresocks.ParseReserved(opts.Reserved) @@ -208,35 +231,55 @@ func runWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoint str } 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 + var werr error + var tunDev tun.Device + for _, t := range []string{"t1", "t2"} { + // Create a new tun interface + tunDev, werr = newNormalTun([]netip.Addr{opts.DnsAddr}) + if werr != nil { + continue + } + + // Create userspace tun network stack + werr = establishWireguard(l, &conf, tunDev, true, opts.FwMark, t) + if werr != nil { + continue + } + break + } + if werr != nil { + return werr } 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 - } + var werr error + var tnet *netstack.Net + var tunDev tun.Device + for _, t := range []string{"t1", "t2"} { + tunDev, tnet, werr = netstack.CreateNetTUN(conf.Interface.Addresses, conf.Interface.DNS, conf.Interface.MTU) + if werr != nil { + continue + } - // // Test wireguard connectivity - // if err := usermodeTunTest(ctx, l, tnet); err != nil { - // return err - // } + werr = establishWireguard(l, &conf, tunDev, false, opts.FwMark, t) + if werr != nil { + continue + } + + // Test wireguard connectivity + werr = usermodeTunTest(ctx, l, tnet) + if werr != nil { + continue + } + break + } + if werr != nil { + return werr + } // Run a proxy on the userspace stack _, err = wiresocks.StartProxy(ctx, l, tnet, opts.Bind) @@ -267,7 +310,7 @@ func runWarpInWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoi for i, peer := range conf.Peers { peer.Endpoint = endpoints[0] peer.Trick = true - peer.KeepAlive = 3 + peer.KeepAlive = 5 if opts.Reserved != "" { r, err := wiresocks.ParseReserved(opts.Reserved) @@ -280,24 +323,35 @@ func runWarpInWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoi conf.Peers[i] = peer } - // Create userspace tun network stack - tunDev, tnet, err := newUsermodeTun(&conf) - if err != nil { - return err - } - // 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 - } + var werr error + var tnet1 *netstack.Net + var tunDev tun.Device + for _, t := range []string{"t1", "t2"} { + // Create userspace tun network stack + tunDev, tnet1, werr = netstack.CreateNetTUN(conf.Interface.Addresses, conf.Interface.DNS, conf.Interface.MTU) + if werr != nil { + continue + } - // // Test wireguard connectivity - // if err := usermodeTunTest(ctx, l, tnet); err != nil { - // return err - // } + werr = establishWireguard(l.With("gool", "outer"), &conf, tunDev, opts.Tun, opts.FwMark, t) + if werr != nil { + continue + } + + // Test wireguard connectivity + werr = usermodeTunTest(ctx, l, tnet1) + if werr != nil { + continue + } + break + } + if werr != nil { + return werr + } // 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) + addr, err := wiresocks.NewVtunUDPForwarder(ctx, netip.MustParseAddrPort("127.0.0.1:0"), endpoints[0], tnet1, singleMTU) if err != nil { return err } @@ -319,7 +373,7 @@ func runWarpInWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoi // Enable keepalive on all peers in config for i, peer := range conf.Peers { peer.Endpoint = addr.String() - peer.KeepAlive = 10 + peer.KeepAlive = 20 if opts.Reserved != "" { r, err := wiresocks.ParseReserved(opts.Reserved) @@ -341,7 +395,7 @@ func runWarpInWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoi // 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 { + if err := establishWireguard(l.With("gool", "inner"), &conf, tunDev, false, opts.FwMark, "t0"); err != nil { return err } l.Info("serving tun", "interface", "warp0") @@ -349,22 +403,22 @@ func runWarpInWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoi } // Create userspace tun network stack - tunDev, tnet, err = newUsermodeTun(&conf) + tunDev, tnet2, err := netstack.CreateNetTUN(conf.Interface.Addresses, conf.Interface.DNS, conf.Interface.MTU) if err != nil { return err } // Establish wireguard on userspace stack - if err := establishWireguard(l.With("gool", "inner"), &conf, tunDev, false, opts.FwMark); err != nil { + if err := establishWireguard(l.With("gool", "inner"), &conf, tunDev, false, opts.FwMark, "t0"); err != nil { return err } - // // Test wireguard connectivity - // if err := usermodeTunTest(ctx, l, tnet); err != nil { - // return err - // } + // Test wireguard connectivity + if err := usermodeTunTest(ctx, l, tnet2); err != nil { + return err + } - _, err = wiresocks.StartProxy(ctx, l, tnet, opts.Bind) + _, err = wiresocks.StartProxy(ctx, l, tnet2, opts.Bind) if err != nil { return err } @@ -392,7 +446,7 @@ func runWarpWithPsiphon(ctx context.Context, l *slog.Logger, opts WarpOptions, e for i, peer := range conf.Peers { peer.Endpoint = endpoint peer.Trick = true - peer.KeepAlive = 3 + peer.KeepAlive = 5 if opts.Reserved != "" { r, err := wiresocks.ParseReserved(opts.Reserved) @@ -405,21 +459,32 @@ func runWarpWithPsiphon(ctx context.Context, l *slog.Logger, opts WarpOptions, e conf.Peers[i] = peer } - // 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 - } + var werr error + var tnet *netstack.Net + var tunDev tun.Device + for _, t := range []string{"t1", "t2"} { + // Create userspace tun network stack + tunDev, tnet, werr = netstack.CreateNetTUN(conf.Interface.Addresses, conf.Interface.DNS, conf.Interface.MTU) + if werr != nil { + continue + } - // // Test wireguard connectivity - // if err := usermodeTunTest(ctx, l, tnet); err != nil { - // return err - // } + werr = establishWireguard(l, &conf, tunDev, false, opts.FwMark, t) + if werr != nil { + continue + } + + // Test wireguard connectivity + werr = usermodeTunTest(ctx, l, tnet) + if werr != nil { + continue + } + break + } + if werr != nil { + return werr + } // Run a proxy on the userspace stack warpBind, err := wiresocks.StartProxy(ctx, l, tnet, netip.MustParseAddrPort("127.0.0.1:0")) diff --git a/app/wg.go b/app/wg.go index ce3794c0f..e6fb88352 100644 --- a/app/wg.go +++ b/app/wg.go @@ -1,12 +1,13 @@ package app import ( + "bufio" "bytes" "context" "fmt" - "io" "log/slog" "net/http" + "strings" "time" "github.com/bepass-org/warp-plus/wireguard/conn" @@ -16,19 +17,10 @@ import ( "github.com/bepass-org/warp-plus/wiresocks" ) -const connTestEndpoint = "https://www.gstatic.com/generate_204" - -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 { - return nil, nil, err - } - - return tunDev, tnet, nil -} +const connTestEndpoint = "http://1.1.1.1/cdn-cgi/trace" func usermodeTunTest(ctx context.Context, l *slog.Logger, tnet *netstack.Net) error { - ctx, cancel := context.WithDeadline(ctx, time.Now().Add(30*time.Second)) + ctx, cancel := context.WithDeadline(ctx, time.Now().Add(5*time.Second)) defer cancel() for { @@ -42,15 +34,13 @@ func usermodeTunTest(ctx context.Context, l *slog.Logger, tnet *netstack.Net) er DialContext: tnet.DialContext, ResponseHeaderTimeout: 5 * time.Second, }} - resp, err := client.Get(connTestEndpoint) + resp, err := client.Head(connTestEndpoint) if err != nil { - l.Error("connection test failed", "error", err.Error()) + l.Error("connection test failed") continue } - _, err = io.ReadAll(resp.Body) - resp.Body.Close() - if err != nil { - l.Error("connection test failed", "error", err.Error()) + if resp.StatusCode != http.StatusOK { + l.Error("connection test failed") continue } @@ -61,7 +51,49 @@ 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, bind bool, fwmark uint32) error { +func waitHandshake(ctx context.Context, l *slog.Logger, dev *device.Device) error { + lastHandshakeSecs := "0" + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + get, err := dev.IpcGet() + if err != nil { + continue + } + scanner := bufio.NewScanner(strings.NewReader(get)) + for scanner.Scan() { + line := scanner.Text() + if line == "" { + break + } + + key, value, ok := strings.Cut(line, "=") + if !ok { + continue + } + + if key == "last_handshake_time_sec" { + lastHandshakeSecs = value + break + } + } + if lastHandshakeSecs != "0" { + l.Debug("handshake complete") + break + } + + l.Debug("waiting on handshake") + time.Sleep(1 * time.Second) + } + + return nil +} + +func establishWireguard(l *slog.Logger, conf *wiresocks.Configuration, tunDev wgtun.Device, bind bool, fwmark uint32, t string) error { // create the IPC message to establish the wireguard conn var request bytes.Buffer @@ -75,7 +107,7 @@ func establishWireguard(l *slog.Logger, conf *wiresocks.Configuration, tunDev wg request.WriteString(fmt.Sprintf("persistent_keepalive_interval=%d\n", peer.KeepAlive)) request.WriteString(fmt.Sprintf("preshared_key=%s\n", peer.PreSharedKey)) request.WriteString(fmt.Sprintf("endpoint=%s\n", peer.Endpoint)) - request.WriteString(fmt.Sprintf("trick=%t\n", peer.Trick)) + request.WriteString(fmt.Sprintf("trick=%s\n", t)) request.WriteString(fmt.Sprintf("reserved=%d,%d,%d\n", peer.Reserved[0], peer.Reserved[1], peer.Reserved[2])) for _, cidr := range peer.AllowedIPs { @@ -103,5 +135,13 @@ func establishWireguard(l *slog.Logger, conf *wiresocks.Configuration, tunDev wg } } + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(15*time.Second)) + defer cancel() + if err := waitHandshake(ctx, l, dev); err != nil { + dev.BindClose() + dev.Close() + return err + } + return nil } diff --git a/wireguard/device/device.go b/wireguard/device/device.go index aec790646..9250af584 100644 --- a/wireguard/device/device.go +++ b/wireguard/device/device.go @@ -182,9 +182,7 @@ func (device *Device) upLocked() error { device.peers.RLock() for _, peer := range device.peers.keyMap { peer.Start() - if peer.persistentKeepaliveInterval.Load() > 0 { - peer.SendKeepalive() - } + peer.SendHandshakeInitiation(false) } device.peers.RUnlock() return nil diff --git a/wireguard/device/peer.go b/wireguard/device/peer.go index c3d528bbd..b86900de1 100644 --- a/wireguard/device/peer.go +++ b/wireguard/device/peer.go @@ -53,7 +53,7 @@ type Peer struct { inbound *autodrainingInboundQueue // sequential ordering of tun writing } - trick bool + trick string reserved [3]byte cookieGenerator CookieGenerator @@ -115,7 +115,7 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) { return peer, nil } -func (peer *Peer) SendBuffers(buffers [][]byte) error { +func (peer *Peer) SendBuffers(buffers [][]byte, trick bool) error { peer.device.net.RLock() defer peer.device.net.RUnlock() @@ -135,9 +135,11 @@ func (peer *Peer) SendBuffers(buffers [][]byte) error { } peer.endpoint.Unlock() - for i := range buffers { - if len(buffers[i]) > 3 && buffers[i][0] > 0 && buffers[i][0] < 5 { - copy(buffers[i][1:4], peer.reserved[:]) + if !trick { + for i := range buffers { + if len(buffers[i]) > 3 && buffers[i][0] > 0 && buffers[i][0] < 5 { + copy(buffers[i][1:4], peer.reserved[:]) + } } } @@ -153,6 +155,7 @@ func (peer *Peer) SendBuffers(buffers [][]byte) error { } func (peer *Peer) String() string { + return "" // The awful goo that follows is identical to: // // base64Key := base64.StdEncoding.EncodeToString(peer.handshake.remoteStatic[:]) diff --git a/wireguard/device/send.go b/wireguard/device/send.go index c8d5c4bed..459adfcfa 100644 --- a/wireguard/device/send.go +++ b/wireguard/device/send.go @@ -79,29 +79,64 @@ func (elem *QueueOutboundElement) clearPointers() { elem.peer = nil } -func randomInt(min, max int) int { - nBig, err := rand.Int(rand.Reader, big.NewInt(int64(max-min+1))) +func randomInt(min, max uint64) uint64 { + rangee := max - min + if rangee < 1 { + return 0 + } + + n, err := rand.Int(rand.Reader, big.NewInt(int64(rangee))) if err != nil { panic(err) } - return int(nBig.Int64()) + min + + return min + n.Uint64() } func (peer *Peer) sendRandomPackets() { - numPackets := randomInt(8, 15) - randomPacket := make([]byte, 100) - for i := 0; i < numPackets; i++ { + var Wheader = []byte{} + switch peer.trick { + case "t1": + case "t2": + clist := []byte{0xDC, 0xDE, 0xD3, 0xD9, 0xD0, 0xEC, 0xEE, 0xE3} + Wheader = []byte{ + clist[randomInt(0, uint64(len(clist)-1))], + 0x00, 0x00, 0x00, 0x01, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x44, 0xD0, + } + _, err := rand.Read(Wheader[6:14]) + if err != nil { + panic(err) + } + // default: + // if len(tm)%2 != 0 { + // tm = tm + "0" + // } + // decodedBytes, err := hex.DecodeString(tm) + // if err == nil { + // Wheader = decodedBytes + // } + default: + return + } + + numPackets := randomInt(15, 50) + maxpLen := uint64(len(Wheader) + 120) + randomPacket := make([]byte, maxpLen) + for i := uint64(0); i < numPackets; i++ { if peer.device.isClosed() || !peer.isRunning.Load() { return } - packetSize := randomInt(40, 100) - _, err := rand.Read(randomPacket[:packetSize]) + packetSize := randomInt(uint64(len(Wheader)+10), maxpLen) + _, err := rand.Read(randomPacket[len(Wheader):packetSize]) if err != nil { return } + copy(randomPacket[0:], Wheader) - err = peer.SendBuffers([][]byte{randomPacket[:packetSize]}) + err = peer.SendBuffers([][]byte{randomPacket[:packetSize]}, true) if err != nil { return } @@ -114,7 +149,7 @@ func (peer *Peer) sendRandomPackets() { */ func (peer *Peer) SendKeepalive() { if len(peer.queue.staged) == 0 && peer.isRunning.Load() { - if peer.trick { + if peer.trick != "" && peer.trick != "t0" { peer.device.log.Verbosef("%v - Running tricks! (keepalive)", peer) peer.sendRandomPackets() } @@ -152,7 +187,7 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error { return nil } - if peer.trick { + if peer.trick != "" && peer.trick != "t0" { peer.device.log.Verbosef("%v - Running tricks! (handshake)", peer) peer.sendRandomPackets() } @@ -177,7 +212,7 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error { peer.timersAnyAuthenticatedPacketTraversal() peer.timersAnyAuthenticatedPacketSent() - err = peer.SendBuffers([][]byte{packet}) + err = peer.SendBuffers([][]byte{packet}, false) if err != nil { peer.device.log.Errorf("%v - Failed to send handshake initiation: %v", peer, err) } @@ -216,7 +251,7 @@ func (peer *Peer) SendHandshakeResponse() error { peer.timersAnyAuthenticatedPacketSent() // TODO: allocation could be avoided - err = peer.SendBuffers([][]byte{packet}) + err = peer.SendBuffers([][]byte{packet}, false) if err != nil { peer.device.log.Errorf("%v - Failed to send handshake response: %v", peer, err) } @@ -564,7 +599,7 @@ func (peer *Peer) RoutineSequentialSender(maxBatchSize int) { peer.timersAnyAuthenticatedPacketTraversal() peer.timersAnyAuthenticatedPacketSent() - err := peer.SendBuffers(bufs) + err := peer.SendBuffers(bufs, false) if dataSent { peer.timersDataSent() } diff --git a/wireguard/device/uapi.go b/wireguard/device/uapi.go index f917b6771..1022cd968 100644 --- a/wireguard/device/uapi.go +++ b/wireguard/device/uapi.go @@ -119,7 +119,7 @@ func (device *Device) IpcGetOperation(w io.Writer) error { sendf("tx_bytes=%d", peer.txBytes.Load()) sendf("rx_bytes=%d", peer.rxBytes.Load()) sendf("persistent_keepalive_interval=%d", peer.persistentKeepaliveInterval.Load()) - sendf("trick=%t", peer.trick) + sendf("trick=%s", peer.trick) sendf("reserved=%d,%d,%d", peer.reserved[0], peer.reserved[1], peer.reserved[2]) device.allowedips.EntriesForPeer(peer, func(prefix netip.Prefix) bool { @@ -266,9 +266,7 @@ func (peer *ipcSetPeer) handlePostConfig() { } if peer.device.isUp() { peer.Start() - if peer.pkaOn { - peer.SendKeepalive() - } + peer.SendHandshakeInitiation(false) peer.SendStagedPackets() } } @@ -389,12 +387,8 @@ func (device *Device) handlePeerLine(peer *ipcSetPeer, key, value string) error } case "trick": - device.log.Verbosef("%v - UAPI: Setting trick: %s", peer.Peer, value) - parsedBool, err := strconv.ParseBool(value) - if err != nil { - return ipcErrorf(ipc.IpcErrorInvalid, "invalid trick value: %v", value) - } - peer.trick = parsedBool + device.log.Verbosef("%v - UAPI: Setting trick", peer.Peer) + peer.trick = value case "reserved": device.log.Verbosef("%v - UAPI: Setting reserved: %s", peer.Peer, value)