Sockopt: Fix Darwin (macOS, iOS...) UDP interface bind (#4530)

Fixes https://github.com/XTLS/Xray-core/issues/4007
This commit is contained in:
92613hjh 2025-03-31 19:19:06 +08:00 committed by GitHub
parent 8284a0ef8f
commit 63eb0539b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,7 +1,7 @@
package internet
import (
network "net"
gonet "net"
"os"
"syscall"
"unsafe"
@ -108,13 +108,58 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf
return err
}
}
if config.Interface != "" {
InterfaceIndex := getInterfaceIndexByName(config.Interface)
if InterfaceIndex != 0 {
if err := unix.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BOUND_IF, InterfaceIndex); err != nil {
return errors.New("failed to set Interface").Base(err)
if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 {
if config.TcpKeepAliveIdle > 0 {
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_KEEPALIVE, int(config.TcpKeepAliveInterval)); err != nil {
return errors.New("failed to set TCP_KEEPINTVL", err)
}
}
if config.TcpKeepAliveInterval > 0 {
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, sysTCP_KEEPINTVL, int(config.TcpKeepAliveIdle)); err != nil {
return errors.New("failed to set TCP_KEEPIDLE", err)
}
}
if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 1); err != nil {
return errors.New("failed to set SO_KEEPALIVE", err)
}
} else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 {
if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 0); err != nil {
return errors.New("failed to unset SO_KEEPALIVE", err)
}
}
}
if config.Interface != "" {
iface, err := gonet.InterfaceByName(config.Interface)
if err != nil {
return errors.New("failed to get interface ", config.Interface).Base(err)
}
if network == "tcp6" || network == "udp6" {
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, iface.Index); err != nil {
return errors.New("failed to set IPV6_BOUND_IF").Base(err)
}
} else {
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, iface.Index); err != nil {
return errors.New("failed to set IP_BOUND_IF").Base(err)
}
}
}
return nil
}
func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error {
if isTCPSocket(network) {
tfo := config.ParseTFOValue()
if tfo > 0 {
tfo = TCP_FASTOPEN_SERVER
}
if tfo >= 0 {
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_FASTOPEN, tfo); err != nil {
return err
}
}
if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 {
@ -138,46 +183,19 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf
}
}
return nil
}
if config.Interface != "" {
iface, err := gonet.InterfaceByName(config.Interface)
func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error {
if isTCPSocket(network) {
tfo := config.ParseTFOValue()
if tfo > 0 {
tfo = TCP_FASTOPEN_SERVER
if err != nil {
return errors.New("failed to get interface ", config.Interface).Base(err)
}
if tfo >= 0 {
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_FASTOPEN, tfo); err != nil {
return err
if network == "tcp6" || network == "udp6" {
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, iface.Index); err != nil {
return errors.New("failed to set IPV6_BOUND_IF").Base(err)
}
}
if config.Interface != "" {
InterfaceIndex := getInterfaceIndexByName(config.Interface)
if InterfaceIndex != 0 {
if err := unix.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BOUND_IF, InterfaceIndex); err != nil {
return errors.New("failed to set Interface").Base(err)
}
}
}
if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 {
if config.TcpKeepAliveIdle > 0 {
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_KEEPALIVE, int(config.TcpKeepAliveInterval)); err != nil {
return errors.New("failed to set TCP_KEEPINTVL", err)
}
}
if config.TcpKeepAliveInterval > 0 {
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, sysTCP_KEEPINTVL, int(config.TcpKeepAliveIdle)); err != nil {
return errors.New("failed to set TCP_KEEPIDLE", err)
}
}
if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 1); err != nil {
return errors.New("failed to set SO_KEEPALIVE", err)
}
} else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 {
if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 0); err != nil {
return errors.New("failed to unset SO_KEEPALIVE", err)
} else {
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, iface.Index); err != nil {
return errors.New("failed to set IP_BOUND_IF").Base(err)
}
}
}
@ -224,24 +242,3 @@ func setReusePort(fd uintptr) error {
}
return nil
}
func getInterfaceIndexByName(name string) int {
ifaces, err := network.Interfaces()
if err == nil {
for _, iface := range ifaces {
if (iface.Flags&network.FlagUp == network.FlagUp) && (iface.Flags&network.FlagLoopback != network.FlagLoopback) {
addrs, _ := iface.Addrs()
for _, addr := range addrs {
if ipnet, ok := addr.(*network.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
if iface.Name == name {
return iface.Index
}
}
}
}
}
}
}
return 0
}