github.com/xmplusdev/xmcore@v1.8.11-0.20240412132628-5518b55526af/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 49 func (nl *pfiocNatlook) rdPort() int { 50 return int(binary.BigEndian.Uint16((*[2]byte)(unsafe.Pointer(&nl.Rdport))[:])) 51 } 52 53 func (nl *pfiocNatlook) setPort(remote, local int) { 54 binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&nl.Sport))[:], uint16(remote)) 55 binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&nl.Dport))[:], uint16(local)) 56 } 57 58 // OriginalDst uses ioctl to read original destination from /dev/pf 59 func OriginalDst(la, ra net.Addr) (net.IP, int, error) { 60 f, err := os.Open("/dev/pf") 61 if err != nil { 62 return net.IP{}, -1, newError("failed to open device /dev/pf").Base(err) 63 } 64 defer f.Close() 65 fd := f.Fd() 66 b := make([]byte, sizeofPfiocNatlook) 67 nl := (*pfiocNatlook)(unsafe.Pointer(&b[0])) 68 var raIP, laIP net.IP 69 var raPort, laPort int 70 switch la.(type) { 71 case *net.TCPAddr: 72 raIP = ra.(*net.TCPAddr).IP 73 laIP = la.(*net.TCPAddr).IP 74 raPort = ra.(*net.TCPAddr).Port 75 laPort = la.(*net.TCPAddr).Port 76 nl.Proto = syscall.IPPROTO_TCP 77 case *net.UDPAddr: 78 raIP = ra.(*net.UDPAddr).IP 79 laIP = la.(*net.UDPAddr).IP 80 raPort = ra.(*net.UDPAddr).Port 81 laPort = la.(*net.UDPAddr).Port 82 nl.Proto = syscall.IPPROTO_UDP 83 } 84 if raIP.To4() != nil { 85 if laIP.IsUnspecified() { 86 laIP = net.ParseIP("127.0.0.1") 87 } 88 copy(nl.Saddr[:net.IPv4len], raIP.To4()) 89 copy(nl.Daddr[:net.IPv4len], laIP.To4()) 90 nl.Af = syscall.AF_INET 91 } 92 if raIP.To16() != nil && raIP.To4() == nil { 93 if laIP.IsUnspecified() { 94 laIP = net.ParseIP("::1") 95 } 96 copy(nl.Saddr[:], raIP) 97 copy(nl.Daddr[:], laIP) 98 nl.Af = syscall.AF_INET6 99 } 100 nl.setPort(raPort, laPort) 101 ioc := uintptr(sysDIOCNATLOOK) 102 for _, dir := range []byte{sysPFOUT, sysPFIN} { 103 nl.Direction = dir 104 err = ioctl(fd, int(ioc), b) 105 if err == nil || err != syscall.ENOENT { 106 break 107 } 108 } 109 if err != nil { 110 return net.IP{}, -1, os.NewSyscallError("ioctl", err) 111 } 112 113 odPort := nl.rdPort() 114 var odIP net.IP 115 switch nl.Af { 116 case syscall.AF_INET: 117 odIP = make(net.IP, net.IPv4len) 118 copy(odIP, nl.Rdaddr[:net.IPv4len]) 119 case syscall.AF_INET6: 120 odIP = make(net.IP, net.IPv6len) 121 copy(odIP, nl.Rdaddr[:]) 122 } 123 return odIP, odPort, nil 124 } 125 126 func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error { 127 if config.Mark != 0 { 128 if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_USER_COOKIE, int(config.Mark)); err != nil { 129 return newError("failed to set SO_USER_COOKIE").Base(err) 130 } 131 } 132 133 if isTCPSocket(network) { 134 tfo := config.ParseTFOValue() 135 if tfo > 0 { 136 tfo = 1 137 } 138 if tfo >= 0 { 139 if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, tfo); err != nil { 140 return newError("failed to set TCP_FASTOPEN_CONNECT=", tfo).Base(err) 141 } 142 } 143 if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 { 144 if config.TcpKeepAliveIdle > 0 { 145 if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil { 146 return newError("failed to set TCP_KEEPIDLE", err) 147 } 148 } 149 if config.TcpKeepAliveInterval > 0 { 150 if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { 151 return newError("failed to set TCP_KEEPINTVL", err) 152 } 153 } 154 if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { 155 return newError("failed to set SO_KEEPALIVE", err) 156 } 157 } else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 { 158 if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil { 159 return newError("failed to unset SO_KEEPALIVE", err) 160 } 161 } 162 } 163 164 if config.Tproxy.IsEnabled() { 165 ip, _, _ := net.SplitHostPort(address) 166 if net.ParseIP(ip).To4() != nil { 167 if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BINDANY, 1); err != nil { 168 return newError("failed to set outbound IP_BINDANY").Base(err) 169 } 170 } else { 171 if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BINDANY, 1); err != nil { 172 return newError("failed to set outbound IPV6_BINDANY").Base(err) 173 } 174 } 175 } 176 return nil 177 } 178 179 func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error { 180 if config.Mark != 0 { 181 if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_USER_COOKIE, int(config.Mark)); err != nil { 182 return newError("failed to set SO_USER_COOKIE").Base(err) 183 } 184 } 185 if isTCPSocket(network) { 186 tfo := config.ParseTFOValue() 187 if tfo >= 0 { 188 if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, tfo); err != nil { 189 return newError("failed to set TCP_FASTOPEN=", tfo).Base(err) 190 } 191 } 192 if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 { 193 if config.TcpKeepAliveIdle > 0 { 194 if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil { 195 return newError("failed to set TCP_KEEPIDLE", err) 196 } 197 } 198 if config.TcpKeepAliveInterval > 0 { 199 if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { 200 return newError("failed to set TCP_KEEPINTVL", err) 201 } 202 } 203 if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { 204 return newError("failed to set SO_KEEPALIVE", err) 205 } 206 } else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 { 207 if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil { 208 return newError("failed to unset SO_KEEPALIVE", err) 209 } 210 } 211 } 212 213 if config.Tproxy.IsEnabled() { 214 if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BINDANY, 1); err != nil { 215 if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BINDANY, 1); err != nil { 216 return newError("failed to set inbound IP_BINDANY").Base(err) 217 } 218 } 219 } 220 221 return nil 222 } 223 224 func bindAddr(fd uintptr, ip []byte, port uint32) error { 225 setReuseAddr(fd) 226 setReusePort(fd) 227 228 var sockaddr syscall.Sockaddr 229 230 switch len(ip) { 231 case net.IPv4len: 232 a4 := &syscall.SockaddrInet4{ 233 Port: int(port), 234 } 235 copy(a4.Addr[:], ip) 236 sockaddr = a4 237 case net.IPv6len: 238 a6 := &syscall.SockaddrInet6{ 239 Port: int(port), 240 } 241 copy(a6.Addr[:], ip) 242 sockaddr = a6 243 default: 244 return newError("unexpected length of ip") 245 } 246 247 return syscall.Bind(int(fd), sockaddr) 248 } 249 250 func setReuseAddr(fd uintptr) error { 251 if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { 252 return newError("failed to set SO_REUSEADDR").Base(err).AtWarning() 253 } 254 return nil 255 } 256 257 func setReusePort(fd uintptr) error { 258 if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePortLB, 1); err != nil { 259 if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePort, 1); err != nil { 260 return newError("failed to set SO_REUSEPORT").Base(err).AtWarning() 261 } 262 } 263 return nil 264 }