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

     1  //go:build darwin || linux
     2  
     3  package conn
     4  
     5  import (
     6  	"fmt"
     7  	"net/netip"
     8  	"unsafe"
     9  
    10  	"golang.org/x/sys/unix"
    11  )
    12  
    13  // SocketControlMessageBufferSize specifies the buffer size for receiving socket control messages.
    14  const SocketControlMessageBufferSize = unix.SizeofCmsghdr + (unix.SizeofInet6Pktinfo+unix.SizeofPtr-1) & ^(unix.SizeofPtr-1)
    15  
    16  // ParsePktinfoCmsg parses a single socket control message of type IP_PKTINFO or IPV6_PKTINFO,
    17  // and returns the IP address and index of the network interface the packet was received from,
    18  // or an error.
    19  //
    20  // This function is only implemented for Linux, macOS and Windows. On other platforms, this is a no-op.
    21  func ParsePktinfoCmsg(cmsg []byte) (netip.Addr, uint32, error) {
    22  	if len(cmsg) < unix.SizeofCmsghdr {
    23  		return netip.Addr{}, 0, fmt.Errorf("control message length %d is shorter than cmsghdr length", len(cmsg))
    24  	}
    25  
    26  	cmsghdr := (*unix.Cmsghdr)(unsafe.Pointer(&cmsg[0]))
    27  
    28  	switch {
    29  	case cmsghdr.Level == unix.IPPROTO_IP && cmsghdr.Type == unix.IP_PKTINFO && len(cmsg) >= unix.SizeofCmsghdr+unix.SizeofInet4Pktinfo:
    30  		pktinfo := (*unix.Inet4Pktinfo)(unsafe.Pointer(&cmsg[unix.SizeofCmsghdr]))
    31  		return netip.AddrFrom4(pktinfo.Spec_dst), uint32(pktinfo.Ifindex), nil
    32  
    33  	case cmsghdr.Level == unix.IPPROTO_IPV6 && cmsghdr.Type == unix.IPV6_PKTINFO && len(cmsg) >= unix.SizeofCmsghdr+unix.SizeofInet6Pktinfo:
    34  		pktinfo := (*unix.Inet6Pktinfo)(unsafe.Pointer(&cmsg[unix.SizeofCmsghdr]))
    35  		return netip.AddrFrom16(pktinfo.Addr), pktinfo.Ifindex, nil
    36  
    37  	default:
    38  		return netip.Addr{}, 0, fmt.Errorf("unknown control message level %d type %d", cmsghdr.Level, cmsghdr.Type)
    39  	}
    40  }