github.com/nxtrace/NTrace-core@v1.3.1-0.20240513132635-39169291e8c9/trace/internal/icmp_darwin.go (about)

     1  //go:build darwin
     2  
     3  package internal
     4  
     5  import (
     6  	"context"
     7  	"errors"
     8  	"net"
     9  	"os"
    10  	"syscall"
    11  	"unsafe"
    12  )
    13  
    14  //go:linkname internetSocket net.internetSocket
    15  func internetSocket(ctx context.Context, net string, laddr, raddr any, sotype, proto int, mode string, ctrlCtxFn func(context.Context, string, string, syscall.RawConn) error) (fd unsafe.Pointer, err error)
    16  
    17  //go:linkname newIPConn net.newIPConn
    18  func newIPConn(fd unsafe.Pointer) *net.IPConn
    19  
    20  var (
    21  	errUnknownNetwork = errors.New("unknown network type")
    22  	errUnknownIface   = errors.New("unknown network interface")
    23  
    24  	networkMap = map[string]string{
    25  		"ip4:icmp": "udp4",
    26  		"ip4:1":    "udp4",
    27  		"ip6:icmp": "udp6",
    28  		"ip6:58":   "udp6",
    29  	}
    30  )
    31  
    32  func ListenICMP(network string, laddr string) (net.PacketConn, error) {
    33  	if os.Getuid() == 0 { // root
    34  		return net.ListenPacket(network, laddr)
    35  	} else {
    36  		if nw, ok := networkMap[network]; ok {
    37  			proto := syscall.IPPROTO_ICMP
    38  			if nw == "udp6" {
    39  				proto = syscall.IPPROTO_ICMPV6
    40  			}
    41  
    42  			var ifIndex = -1
    43  			if laddr != "" {
    44  				la := net.ParseIP(laddr)
    45  				if ifaces, err := net.Interfaces(); err == nil {
    46  					for _, iface := range ifaces {
    47  						addrs, err := iface.Addrs()
    48  						if err != nil {
    49  							continue
    50  						}
    51  						for _, addr := range addrs {
    52  							if ipnet, ok := addr.(*net.IPNet); ok {
    53  								if ipnet.IP.Equal(la) {
    54  									ifIndex = iface.Index
    55  									break
    56  								}
    57  							}
    58  						}
    59  					}
    60  					if ifIndex == -1 {
    61  						return nil, errUnknownIface
    62  					}
    63  				} else {
    64  					return nil, err
    65  				}
    66  			}
    67  
    68  			isock, err := internetSocket(context.Background(), nw, nil, nil, syscall.SOCK_DGRAM, proto, "listen",
    69  				func(ctx context.Context, network, address string, c syscall.RawConn) error {
    70  					if ifIndex != -1 {
    71  						if proto == syscall.IPPROTO_ICMP {
    72  							return c.Control(func(fd uintptr) {
    73  								err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BOUND_IF, ifIndex)
    74  								if err != nil {
    75  									return
    76  								}
    77  							})
    78  						} else {
    79  							return c.Control(func(fd uintptr) {
    80  								err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BOUND_IF, ifIndex)
    81  								if err != nil {
    82  									return
    83  								}
    84  							})
    85  						}
    86  					}
    87  					return nil
    88  				})
    89  			if err != nil {
    90  				panic(err)
    91  			}
    92  			return newIPConn(isock), nil
    93  		} else {
    94  			return nil, errUnknownNetwork
    95  		}
    96  	}
    97  }