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  }