github.com/eagleql/xray-core@v1.4.4/transport/internet/sockopt_freebsd.go (about)

     1  package internet
     2  
     3  import (
     4  	"encoding/binary"
     5  	"net"
     6  	"os"
     7  	"syscall"
     8  	"unsafe"
     9  
    10  	"golang.org/x/sys/unix"
    11  )
    12  
    13  const (
    14  	sysPFINOUT     = 0x0
    15  	sysPFIN        = 0x1
    16  	sysPFOUT       = 0x2
    17  	sysPFFWD       = 0x3
    18  	sysDIOCNATLOOK = 0xc04c4417
    19  )
    20  
    21  type pfiocNatlook struct {
    22  	Saddr     [16]byte /* pf_addr */
    23  	Daddr     [16]byte /* pf_addr */
    24  	Rsaddr    [16]byte /* pf_addr */
    25  	Rdaddr    [16]byte /* pf_addr */
    26  	Sport     uint16
    27  	Dport     uint16
    28  	Rsport    uint16
    29  	Rdport    uint16
    30  	Af        uint8
    31  	Proto     uint8
    32  	Direction uint8
    33  	Pad       [1]byte
    34  }
    35  
    36  const (
    37  	sizeofPfiocNatlook = 0x4c
    38  	soReUsePort        = 0x00000200
    39  	soReUsePortLB      = 0x00010000
    40  )
    41  
    42  func ioctl(s uintptr, ioc int, b []byte) error {
    43  	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, s, uintptr(ioc), uintptr(unsafe.Pointer(&b[0]))); errno != 0 {
    44  		return error(errno)
    45  	}
    46  	return nil
    47  }
    48  func (nl *pfiocNatlook) rdPort() int {
    49  	return int(binary.BigEndian.Uint16((*[2]byte)(unsafe.Pointer(&nl.Rdport))[:]))
    50  }
    51  
    52  func (nl *pfiocNatlook) setPort(remote, local int) {
    53  	binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&nl.Sport))[:], uint16(remote))
    54  	binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&nl.Dport))[:], uint16(local))
    55  }
    56  
    57  // OriginalDst uses ioctl to read original destination from /dev/pf
    58  func OriginalDst(la, ra net.Addr) (net.IP, int, error) {
    59  	f, err := os.Open("/dev/pf")
    60  	if err != nil {
    61  		return net.IP{}, -1, newError("failed to open device /dev/pf").Base(err)
    62  	}
    63  	defer f.Close()
    64  	fd := f.Fd()
    65  	b := make([]byte, sizeofPfiocNatlook)
    66  	nl := (*pfiocNatlook)(unsafe.Pointer(&b[0]))
    67  	var raIP, laIP net.IP
    68  	var raPort, laPort int
    69  	switch la.(type) {
    70  	case *net.TCPAddr:
    71  		raIP = ra.(*net.TCPAddr).IP
    72  		laIP = la.(*net.TCPAddr).IP
    73  		raPort = ra.(*net.TCPAddr).Port
    74  		laPort = la.(*net.TCPAddr).Port
    75  		nl.Proto = syscall.IPPROTO_TCP
    76  	case *net.UDPAddr:
    77  		raIP = ra.(*net.UDPAddr).IP
    78  		laIP = la.(*net.UDPAddr).IP
    79  		raPort = ra.(*net.UDPAddr).Port
    80  		laPort = la.(*net.UDPAddr).Port
    81  		nl.Proto = syscall.IPPROTO_UDP
    82  	}
    83  	if raIP.To4() != nil {
    84  		if laIP.IsUnspecified() {
    85  			laIP = net.ParseIP("127.0.0.1")
    86  		}
    87  		copy(nl.Saddr[:net.IPv4len], raIP.To4())
    88  		copy(nl.Daddr[:net.IPv4len], laIP.To4())
    89  		nl.Af = syscall.AF_INET
    90  	}
    91  	if raIP.To16() != nil && raIP.To4() == nil {
    92  		if laIP.IsUnspecified() {
    93  			laIP = net.ParseIP("::1")
    94  		}
    95  		copy(nl.Saddr[:], raIP)
    96  		copy(nl.Daddr[:], laIP)
    97  		nl.Af = syscall.AF_INET6
    98  	}
    99  	nl.setPort(raPort, laPort)
   100  	ioc := uintptr(sysDIOCNATLOOK)
   101  	for _, dir := range []byte{sysPFOUT, sysPFIN} {
   102  		nl.Direction = dir
   103  		err = ioctl(fd, int(ioc), b)
   104  		if err == nil || err != syscall.ENOENT {
   105  			break
   106  		}
   107  	}
   108  	if err != nil {
   109  		return net.IP{}, -1, os.NewSyscallError("ioctl", err)
   110  	}
   111  
   112  	odPort := nl.rdPort()
   113  	var odIP net.IP
   114  	switch nl.Af {
   115  	case syscall.AF_INET:
   116  		odIP = make(net.IP, net.IPv4len)
   117  		copy(odIP, nl.Rdaddr[:net.IPv4len])
   118  	case syscall.AF_INET6:
   119  		odIP = make(net.IP, net.IPv6len)
   120  		copy(odIP, nl.Rdaddr[:])
   121  	}
   122  	return odIP, odPort, nil
   123  }
   124  
   125  func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error {
   126  	if config.Mark != 0 {
   127  		if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_USER_COOKIE, int(config.Mark)); err != nil {
   128  			return newError("failed to set SO_USER_COOKIE").Base(err)
   129  		}
   130  	}
   131  
   132  	if isTCPSocket(network) {
   133  		tfo := config.ParseTFOValue()
   134  		if tfo > 0 {
   135  			tfo = 1
   136  		}
   137  		if tfo >= 0 {
   138  			if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, tfo); err != nil {
   139  				return newError("failed to set TCP_FASTOPEN_CONNECT=", tfo).Base(err)
   140  			}
   141  		}
   142  	}
   143  
   144  	if config.Tproxy.IsEnabled() {
   145  		ip, _, _ := net.SplitHostPort(address)
   146  		if net.ParseIP(ip).To4() != nil {
   147  			if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BINDANY, 1); err != nil {
   148  				return newError("failed to set outbound IP_BINDANY").Base(err)
   149  			}
   150  		} else {
   151  			if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BINDANY, 1); err != nil {
   152  				return newError("failed to set outbound IPV6_BINDANY").Base(err)
   153  			}
   154  		}
   155  	}
   156  	return nil
   157  }
   158  
   159  func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error {
   160  	if config.Mark != 0 {
   161  		if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_USER_COOKIE, int(config.Mark)); err != nil {
   162  			return newError("failed to set SO_USER_COOKIE").Base(err)
   163  		}
   164  	}
   165  	if isTCPSocket(network) {
   166  		tfo := config.ParseTFOValue()
   167  		if tfo >= 0 {
   168  			if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, tfo); err != nil {
   169  				return newError("failed to set TCP_FASTOPEN=", tfo).Base(err)
   170  			}
   171  		}
   172  	}
   173  
   174  	if config.Tproxy.IsEnabled() {
   175  		if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BINDANY, 1); err != nil {
   176  			if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BINDANY, 1); err != nil {
   177  				return newError("failed to set inbound IP_BINDANY").Base(err)
   178  			}
   179  		}
   180  	}
   181  
   182  	return nil
   183  }
   184  
   185  func bindAddr(fd uintptr, ip []byte, port uint32) error {
   186  	setReuseAddr(fd)
   187  	setReusePort(fd)
   188  
   189  	var sockaddr syscall.Sockaddr
   190  
   191  	switch len(ip) {
   192  	case net.IPv4len:
   193  		a4 := &syscall.SockaddrInet4{
   194  			Port: int(port),
   195  		}
   196  		copy(a4.Addr[:], ip)
   197  		sockaddr = a4
   198  	case net.IPv6len:
   199  		a6 := &syscall.SockaddrInet6{
   200  			Port: int(port),
   201  		}
   202  		copy(a6.Addr[:], ip)
   203  		sockaddr = a6
   204  	default:
   205  		return newError("unexpected length of ip")
   206  	}
   207  
   208  	return syscall.Bind(int(fd), sockaddr)
   209  }
   210  
   211  func setReuseAddr(fd uintptr) error {
   212  	if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
   213  		return newError("failed to set SO_REUSEADDR").Base(err).AtWarning()
   214  	}
   215  	return nil
   216  }
   217  
   218  func setReusePort(fd uintptr) error {
   219  	if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePortLB, 1); err != nil {
   220  		if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePort, 1); err != nil {
   221  			return newError("failed to set SO_REUSEPORT").Base(err).AtWarning()
   222  		}
   223  	}
   224  	return nil
   225  }