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 }