github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/net/proxy/redir/nfutil/nf_linux.go (about) 1 package nfutil 2 3 import ( 4 "net" 5 "syscall" 6 "unsafe" 7 ) 8 9 // Get the original destination of a TCP connection redirected by Netfilter. 10 func GetOrigDst(c *net.TCPConn, ipv6 bool) (*net.TCPAddr, error) { 11 rc, err := c.SyscallConn() 12 if err != nil { 13 return nil, err 14 } 15 var addr *net.TCPAddr 16 err = rc.Control(func(fd uintptr) { 17 if ipv6 { 18 addr, err = ipv6_getorigdst(fd) 19 } else { 20 addr, err = getorigdst(fd) 21 } 22 }) 23 return addr, err 24 } 25 26 // Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c 27 func getorigdst(fd uintptr) (*net.TCPAddr, error) { 28 const _SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv4.h 29 var raw syscall.RawSockaddrInet4 30 siz := unsafe.Sizeof(raw) 31 if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IP, _SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0); err != nil { 32 return nil, err 33 } 34 var addr net.TCPAddr 35 addr.IP = raw.Addr[:] 36 port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // raw.Port is big-endian 37 addr.Port = int(port[0])<<8 | int(port[1]) 38 return &addr, nil 39 } 40 41 // Call ipv6_getorigdst() from linux/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c 42 // NOTE: I haven't tried yet but it should work since Linux 3.8. 43 func ipv6_getorigdst(fd uintptr) (*net.TCPAddr, error) { 44 const _IP6T_SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv6/ip6_tables.h 45 var raw syscall.RawSockaddrInet6 46 siz := unsafe.Sizeof(raw) 47 if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IPV6, _IP6T_SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0); err != nil { 48 return nil, err 49 } 50 var addr net.TCPAddr 51 addr.IP = raw.Addr[:] 52 port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // raw.Port is big-endian 53 addr.Port = int(port[0])<<8 | int(port[1]) 54 return &addr, nil 55 }