TCP Desync
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/unixhas 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
10a20bce
E split-desync: setsockopt failed: invalid argument 🤕
~I guess it related to address family.~
s.ttl is set to 0.
IPv4 traceroute and cache are broken. 🤕 I don't know how to deal with dual-stack socket.
@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)
}
Don't use dual-stack socket, the type of from is always
*syscall.SockaddrInet6, and in a received control message,Cmsghdr.LevelisIPPROTO_IPV6, butsock_extended_err.ee_originmay beSO_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).
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