github.com/database64128/shadowsocks-go@v1.10.2-0.20240315062903-143a773533f1/conn/conn_linux.go (about)

     1  package conn
     2  
     3  import (
     4  	"fmt"
     5  	"net/netip"
     6  	"unsafe"
     7  
     8  	"golang.org/x/sys/unix"
     9  )
    10  
    11  // TransparentSocketControlMessageBufferSize specifies the buffer size for receiving IPV6_RECVORIGDSTADDR socket control messages.
    12  const TransparentSocketControlMessageBufferSize = unix.SizeofCmsghdr + (unix.SizeofSockaddrInet6+unix.SizeofPtr-1) & ^(unix.SizeofPtr-1)
    13  
    14  func setFwmark(fd, fwmark int) error {
    15  	if err := unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_MARK, fwmark); err != nil {
    16  		return fmt.Errorf("failed to set socket option SO_MARK: %w", err)
    17  	}
    18  	return nil
    19  }
    20  
    21  func setTrafficClass(fd int, network string, trafficClass int) error {
    22  	// Set IP_TOS for both v4 and v6.
    23  	if err := unix.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_TOS, trafficClass); err != nil {
    24  		return fmt.Errorf("failed to set socket option IP_TOS: %w", err)
    25  	}
    26  
    27  	switch network {
    28  	case "tcp4", "udp4":
    29  	case "tcp6", "udp6":
    30  		if err := unix.SetsockoptInt(fd, unix.IPPROTO_IPV6, unix.IPV6_TCLASS, trafficClass); err != nil {
    31  			return fmt.Errorf("failed to set socket option IPV6_TCLASS: %w", err)
    32  		}
    33  	default:
    34  		return fmt.Errorf("unsupported network: %s", network)
    35  	}
    36  
    37  	return nil
    38  }
    39  
    40  func setTransparent(fd int, network string) error {
    41  	switch network {
    42  	case "tcp4", "udp4":
    43  		if err := unix.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_TRANSPARENT, 1); err != nil {
    44  			return fmt.Errorf("failed to set socket option IP_TRANSPARENT: %w", err)
    45  		}
    46  	case "tcp6", "udp6":
    47  		if err := unix.SetsockoptInt(fd, unix.IPPROTO_IPV6, unix.IPV6_TRANSPARENT, 1); err != nil {
    48  			return fmt.Errorf("failed to set socket option IPV6_TRANSPARENT: %w", err)
    49  		}
    50  	default:
    51  		return fmt.Errorf("unsupported network: %s", network)
    52  	}
    53  	return nil
    54  }
    55  
    56  func setPMTUD(fd int, network string) error {
    57  	// Set IP_MTU_DISCOVER for both v4 and v6.
    58  	if err := unix.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_MTU_DISCOVER, unix.IP_PMTUDISC_DO); err != nil {
    59  		return fmt.Errorf("failed to set socket option IP_MTU_DISCOVER: %w", err)
    60  	}
    61  
    62  	switch network {
    63  	case "tcp4", "udp4":
    64  	case "tcp6", "udp6":
    65  		if err := unix.SetsockoptInt(fd, unix.IPPROTO_IPV6, unix.IPV6_MTU_DISCOVER, unix.IP_PMTUDISC_DO); err != nil {
    66  			return fmt.Errorf("failed to set socket option IPV6_MTU_DISCOVER: %w", err)
    67  		}
    68  	default:
    69  		return fmt.Errorf("unsupported network: %s", network)
    70  	}
    71  
    72  	return nil
    73  }
    74  
    75  func setRecvPktinfo(fd int, network string) error {
    76  	switch network {
    77  	case "udp4":
    78  		if err := unix.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_PKTINFO, 1); err != nil {
    79  			return fmt.Errorf("failed to set socket option IP_PKTINFO: %w", err)
    80  		}
    81  	case "udp6":
    82  		if err := unix.SetsockoptInt(fd, unix.IPPROTO_IPV6, unix.IPV6_RECVPKTINFO, 1); err != nil {
    83  			return fmt.Errorf("failed to set socket option IPV6_RECVPKTINFO: %w", err)
    84  		}
    85  	default:
    86  		return fmt.Errorf("unsupported network: %s", network)
    87  	}
    88  	return nil
    89  }
    90  
    91  func setRecvOrigDstAddr(fd int, network string) error {
    92  	// Set IP_RECVORIGDSTADDR for both v4 and v6.
    93  	if err := unix.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_RECVORIGDSTADDR, 1); err != nil {
    94  		return fmt.Errorf("failed to set socket option IP_RECVORIGDSTADDR: %w", err)
    95  	}
    96  
    97  	switch network {
    98  	case "udp4":
    99  	case "udp6":
   100  		if err := unix.SetsockoptInt(fd, unix.IPPROTO_IPV6, unix.IPV6_RECVORIGDSTADDR, 1); err != nil {
   101  			return fmt.Errorf("failed to set socket option IPV6_RECVORIGDSTADDR: %w", err)
   102  		}
   103  	default:
   104  		return fmt.Errorf("unsupported network: %s", network)
   105  	}
   106  
   107  	return nil
   108  }
   109  
   110  func (fns setFuncSlice) appendSetTransparentFunc(transparent bool) setFuncSlice {
   111  	if transparent {
   112  		return append(fns, setTransparent)
   113  	}
   114  	return fns
   115  }
   116  
   117  func (fns setFuncSlice) appendSetRecvOrigDstAddrFunc(recvOrigDstAddr bool) setFuncSlice {
   118  	if recvOrigDstAddr {
   119  		return append(fns, setRecvOrigDstAddr)
   120  	}
   121  	return fns
   122  }
   123  
   124  func (lso ListenerSocketOptions) buildSetFns() setFuncSlice {
   125  	return setFuncSlice{}.
   126  		appendSetFwmarkFunc(lso.Fwmark).
   127  		appendSetTrafficClassFunc(lso.TrafficClass).
   128  		appendSetReusePortFunc(lso.ReusePort).
   129  		appendSetTransparentFunc(lso.Transparent).
   130  		appendSetPMTUDFunc(lso.PathMTUDiscovery).
   131  		appendSetRecvPktinfoFunc(lso.ReceivePacketInfo).
   132  		appendSetRecvOrigDstAddrFunc(lso.ReceiveOriginalDestAddr)
   133  }
   134  
   135  func ParseOrigDstAddrCmsg(cmsg []byte) (netip.AddrPort, error) {
   136  	if len(cmsg) < unix.SizeofCmsghdr {
   137  		return netip.AddrPort{}, fmt.Errorf("control message length %d is shorter than cmsghdr length", len(cmsg))
   138  	}
   139  
   140  	cmsghdr := (*unix.Cmsghdr)(unsafe.Pointer(&cmsg[0]))
   141  
   142  	switch {
   143  	case cmsghdr.Level == unix.IPPROTO_IP && cmsghdr.Type == unix.IP_ORIGDSTADDR && len(cmsg) >= unix.SizeofCmsghdr+unix.SizeofSockaddrInet4:
   144  		sa := (*unix.RawSockaddrInet4)(unsafe.Pointer(&cmsg[unix.SizeofCmsghdr]))
   145  		return SockaddrInet4ToAddrPort(sa), nil
   146  
   147  	case cmsghdr.Level == unix.IPPROTO_IPV6 && cmsghdr.Type == unix.IPV6_ORIGDSTADDR && len(cmsg) >= unix.SizeofCmsghdr+unix.SizeofSockaddrInet6:
   148  		sa := (*unix.RawSockaddrInet6)(unsafe.Pointer(&cmsg[unix.SizeofCmsghdr]))
   149  		return SockaddrInet6ToAddrPort(sa), nil
   150  
   151  	default:
   152  		return netip.AddrPort{}, fmt.Errorf("unknown control message level %d type %d", cmsghdr.Level, cmsghdr.Type)
   153  	}
   154  }