firestack icon indicating copy to clipboard operation
firestack copied to clipboard

TCP Desync

Open ignoramous opened this issue 1 year ago • 5 comments

A user writes,

hufrea's method works on older android devices: https://github.com/hufrea/byedpi/blob/82e5229df00859eb3500f0c771a8991d1fe8e607/desync.c#L69-L123

If you will implement TCB-Desynchronization (not just sending in reverse order) , hufrea's method is better.

golang.org/x/sys/unix has exported needed APIs:

package main
import (
	"net"
	"net/netip"
	"time"
	"golang.org/x/sys/unix"
	"flag"
)

func main(){
	var server6 string
	flag.StringVar(&server6,"server6", "[2600:9000:2001:7a00:13:5d53:5740:93a1]:443","must be IPv6")
	flag.Parse()

	dst := net.TCPAddrFromAddrPort(netip.MustParseAddrPort(server6))
	conn,err := net.DialTCP("tcp",nil,dst)
	if err!=nil{
		panic(err)
	}
	defer conn.Close()

	raw,err := conn.SyscallConn()
	if err!=nil{
		panic(err)
	}
	var soFD int
	raw.Control(func(fd uintptr){
		soFD=int(fd)
	})

	fFD,err := unix.MemfdCreate("haha",unix.O_RDWR)
	if err!=nil{
		panic(err)
	}
	defer unix.Close(fFD)
	err = unix.Ftruncate(fFD,4)
	if err!=nil{
		panic(err)
	}

	firstSegment,err := unix.Mmap(fFD,0,4,unix.PROT_WRITE,unix.MAP_SHARED)
	if err!=nil{
		panic(err)
	}
	defer unix.Munmap(firstSegment)
	for i := 0; i<4; i++{
		firstSegment[i]=0
	}

	unix.SetsockoptInt(soFD,unix.SOL_IPV6,unix.IPV6_UNICAST_HOPS,10)
	var t1 int64=0
	_,err = unix.Sendfile(soFD,fFD,&t1,4)
	if err!=nil{
		panic(err)
	}
	for i := 0; i<4; i++{
		firstSegment[i]=1
	}

	unix.SetsockoptInt(soFD,unix.SOL_IPV6,unix.IPV6_UNICAST_HOPS,64)
	secondSegment := [4]byte{5,6,7,8}
	_,err = conn.Write(secondSegment[:])
	if err!=nil{
		panic(err)
	}

	time.Sleep(5000 * time.Millisecond)
}

Currently, firestack has a retrier to deal with incompatible servers. I guess sending two TCP segments in reverse order can improve compatibility too, although the unprivileged implementation increases latency.

To implement it, we can use TTL to force the TCP stack to retransmit.

package main
import (
    "net"
    "net/netip"
    "syscall"
    "time"
)

const SO_ZEROCOPY int = 60
const MSG_ZEROCOPY int = 0x4000000
func main(){
    dst := net.TCPAddrFromAddrPort(netip.MustParseAddrPort("[2600:9000:2375:9a00:14:176d:6100:93a1]:443"))
    conn,err := net.DialTCP("tcp",nil,dst)
    if err!=nil{
        panic(err)
    }
    defer conn.Close()
    
    raw,err := conn.SyscallConn()
    if err!=nil{
        panic(err)
    }
    var soFD int
    raw.Control(func(fd uintptr){
        soFD=int(fd)
    })

    syscall.SetsockoptInt(soFD,syscall.SOL_SOCKET,SO_ZEROCOPY,1)
    syscall.SetsockoptInt(soFD,syscall.SOL_IPV6,syscall.IPV6_UNICAST_HOPS,10)
    firstSegment := [4]byte{0,0,0,0}
    t1 := syscall.SockaddrInet6{}
    syscall.Sendto(soFD,firstSegment[:],MSG_ZEROCOPY,&t1)
    firstSegment[0]=1
    firstSegment[3]=4
    syscall.SetsockoptInt(soFD,syscall.SOL_IPV6,syscall.IPV6_UNICAST_HOPS,64)

    secondSegment := [4]byte{5,6,7,8}
    _,err = conn.Write(secondSegment[:])
    if err!=nil{
        panic(err)
    }

    time.Sleep(5000 * time.Millisecond)
}

If it doesn't combine with ZEROCOPY, we can implement normal TCP Fragmentation; with ZEROCOPY, we can implement TCB-Desynchronization!


Also: #29

ignoramous avatar Apr 09 '24 07:04 ignoramous

10a20bce E split-desync: setsockopt failed: invalid argument 🤕 ~I guess it related to address family.~ s.ttl is set to 0.

Lanius-collaris avatar Jul 26 '24 06:07 Lanius-collaris

IPv4 traceroute and cache are broken. 🤕 I don't know how to deal with dual-stack socket.

Lanius-collaris avatar Jul 26 '24 07:07 Lanius-collaris

@ignoramous Don't use dual-stack socket, the type of from is always *syscall.SockaddrInet6, and in a received control message, Cmsghdr.Level is IPPROTO_IPV6, but sock_extended_err.ee_origin may be SO_EE_ORIGIN_ICMP. So IPv4 traceroute is broken.

package main

import (
	"fmt"
	"net"
	"net/netip"
	//"os"
	"reflect"
	"syscall"
	"time"
)

func p(e error) {
	if e != nil {
		panic(e)
	}
}
func main() {
	lAddr := netip.MustParseAddrPort("0.0.0.0:0")
	conn1, err := net.ListenUDP("udp", net.UDPAddrFromAddrPort(lAddr))
	p(err)
	defer conn1.Close()
	rawConn, _ := conn1.SyscallConn()
	var udpFd int
	rawConn.Control(func(fd uintptr) {
		udpFd = int(fd)
	})
	sockAddr, _ := syscall.Getsockname(udpFd)
	fmt.Printf("Getsockname: %v %v\n", reflect.TypeOf(sockAddr), sockAddr)

	/*err = syscall.SetsockoptInt(udpFd, syscall.IPPROTO_IPV6, syscall.IPV6_RECVERR, 1)
	p(err)*/

	err = syscall.SetsockoptInt(udpFd, syscall.IPPROTO_IP, syscall.IP_RECVERR, 1)
	p(err)

	/*err = syscall.SetsockoptInt(udpFd,syscall.IPPROTO_IPV6,syscall.IPV6_UNICAST_HOPS,1)
	p(err)*/

	err = syscall.SetsockoptInt(udpFd, syscall.IPPROTO_IP, syscall.IP_TTL, 1)
	p(err)

	dst := netip.MustParseAddrPort("140.82.121.4:443")
	conn1.WriteToUDPAddrPort([]byte("haha"), dst)

	time.Sleep(2*time.Second)

	var msgBuf [1024]byte
	var cmsgBuf [1024]byte
	n, cmsgN, _, from, err := syscall.Recvmsg(udpFd, msgBuf[:], cmsgBuf[:], syscall.MSG_ERRQUEUE)
	p(err)
	fmt.Printf("n: %v\ncmsgN: %v\nfrom: %v %v\n", n, cmsgN, reflect.TypeOf(from), from)
	fmt.Printf("msg: %v\ncmsg %v\n", msgBuf[:n], cmsgBuf[:cmsgN])
	//os.WriteFile("cmsg.bin", cmsgBuf[:cmsgN], 0o600)
}

Lanius-collaris avatar Jul 26 '24 09:07 Lanius-collaris

Don't use dual-stack socket, the type of from is always *syscall.SockaddrInet6, and in a received control message, Cmsghdr.Level is IPPROTO_IPV6, but sock_extended_err.ee_origin may be SO_EE_ORIGIN_ICMP. So IPv4 traceroute is broken.

If you've got the time, please do send a PR, as I don't fully grasp these changes to make it happen for the upcoming release (imminent).

ignoramous avatar Jul 26 '24 17:07 ignoramous

In previous test, I applied a simple patch to call Intra.dialStrat().

diff --git a/app/src/full/java/com/celzero/bravedns/ui/fragment/ConfigureFragment.kt b/app/src/full/java/com/celzero/bravedns/ui/fragment/ConfigureFragment.kt
index 5592dcd3..8ee119e9 100644
--- a/app/src/full/java/com/celzero/bravedns/ui/fragment/ConfigureFragment.kt
+++ b/app/src/full/java/com/celzero/bravedns/ui/fragment/ConfigureFragment.kt
@@ -29,6 +29,7 @@ import com.celzero.bravedns.ui.activity.MiscSettingsActivity
 import com.celzero.bravedns.ui.activity.NetworkLogsActivity
 import com.celzero.bravedns.ui.activity.ProxySettingsActivity
 import com.celzero.bravedns.ui.activity.TunnelSettingsActivity
+import intra.Intra
 
 class ConfigureFragment : Fragment(R.layout.fragment_configure) {
 
@@ -87,6 +88,8 @@ class ConfigureFragment : Fragment(R.layout.fragment_configure) {
             // open logs configuration
             startActivity(ScreenType.LOGS)
         }
+        b.enableDesync.setOnClickListener{ Intra.dialStrat(1) }
+        b.enableRetrier.setOnClickListener{ Intra.dialStrat(0) }
     }
 
     private fun startActivity(type: ScreenType) {
diff --git a/app/src/main/res/layout/fragment_configure.xml b/app/src/main/res/layout/fragment_configure.xml
index 8f0d4775..ab86bc25 100644
--- a/app/src/main/res/layout/fragment_configure.xml
+++ b/app/src/main/res/layout/fragment_configure.xml
@@ -336,5 +336,42 @@
                     android:src="@drawable/ic_right_arrow_white" />
             </RelativeLayout>
         </com.google.android.material.card.MaterialCardView>
+        <com.google.android.material.card.MaterialCardView android:id="@+id/enable_desync"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="enable_desync"
+        android:textSize="16sp"
+        app:cardCornerRadius="8dp"
+            app:cardElevation="4dp"
+            app:cardUseCompatPadding="true">
+            <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="20dp">
+            <TextView
+            android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="15dp"
+                    android:text="enable_desync"
+                    android:textColor="?attr/primaryTextColor"
+                    android:textSize="16sp" />
+            </RelativeLayout>
+        </com.google.android.material.card.MaterialCardView>
+        <com.google.android.material.card.MaterialCardView android:id="@+id/enable_retrier"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="enable_retrier"
+        android:textSize="16sp"
+        android:layout_marginBottom="300dp"
+        app:cardCornerRadius="8dp"
+            app:cardElevation="4dp"
+            app:cardUseCompatPadding="true">
+            <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="20dp">
+            <TextView
+            android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="15dp"
+                    android:text="enable_retrier"
+                    android:textColor="?attr/primaryTextColor"
+                    android:textSize="16sp" />
+            </RelativeLayout>
+       </com.google.android.material.card.MaterialCardView>
     </LinearLayout>
 </androidx.core.widget.NestedScrollView>
\ No newline at end of file

Lanius-collaris avatar Jul 27 '24 04:07 Lanius-collaris