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 }