golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/icmp/listen_posix.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows 6 7 package icmp 8 9 import ( 10 "net" 11 "os" 12 "runtime" 13 "syscall" 14 15 "golang.org/x/net/internal/iana" 16 "golang.org/x/net/ipv4" 17 "golang.org/x/net/ipv6" 18 ) 19 20 const sysIP_STRIPHDR = 0x17 // for now only darwin supports this option 21 22 // ListenPacket listens for incoming ICMP packets addressed to 23 // address. See net.Dial for the syntax of address. 24 // 25 // For non-privileged datagram-oriented ICMP endpoints, network must 26 // be "udp4" or "udp6". The endpoint allows to read, write a few 27 // limited ICMP messages such as echo request and echo reply. 28 // Currently only Darwin and Linux support this. 29 // 30 // Examples: 31 // 32 // ListenPacket("udp4", "192.168.0.1") 33 // ListenPacket("udp4", "0.0.0.0") 34 // ListenPacket("udp6", "fe80::1%en0") 35 // ListenPacket("udp6", "::") 36 // 37 // For privileged raw ICMP endpoints, network must be "ip4" or "ip6" 38 // followed by a colon and an ICMP protocol number or name. 39 // 40 // Examples: 41 // 42 // ListenPacket("ip4:icmp", "192.168.0.1") 43 // ListenPacket("ip4:1", "0.0.0.0") 44 // ListenPacket("ip6:ipv6-icmp", "fe80::1%en0") 45 // ListenPacket("ip6:58", "::") 46 func ListenPacket(network, address string) (*PacketConn, error) { 47 var family, proto int 48 switch network { 49 case "udp4": 50 family, proto = syscall.AF_INET, iana.ProtocolICMP 51 case "udp6": 52 family, proto = syscall.AF_INET6, iana.ProtocolIPv6ICMP 53 default: 54 i := last(network, ':') 55 if i < 0 { 56 i = len(network) 57 } 58 switch network[:i] { 59 case "ip4": 60 proto = iana.ProtocolICMP 61 case "ip6": 62 proto = iana.ProtocolIPv6ICMP 63 } 64 } 65 var cerr error 66 var c net.PacketConn 67 switch family { 68 case syscall.AF_INET, syscall.AF_INET6: 69 s, err := syscall.Socket(family, syscall.SOCK_DGRAM, proto) 70 if err != nil { 71 return nil, os.NewSyscallError("socket", err) 72 } 73 if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && family == syscall.AF_INET { 74 if err := syscall.SetsockoptInt(s, iana.ProtocolIP, sysIP_STRIPHDR, 1); err != nil { 75 syscall.Close(s) 76 return nil, os.NewSyscallError("setsockopt", err) 77 } 78 } 79 sa, err := sockaddr(family, address) 80 if err != nil { 81 syscall.Close(s) 82 return nil, err 83 } 84 if err := syscall.Bind(s, sa); err != nil { 85 syscall.Close(s) 86 return nil, os.NewSyscallError("bind", err) 87 } 88 f := os.NewFile(uintptr(s), "datagram-oriented icmp") 89 c, cerr = net.FilePacketConn(f) 90 f.Close() 91 default: 92 c, cerr = net.ListenPacket(network, address) 93 } 94 if cerr != nil { 95 return nil, cerr 96 } 97 switch proto { 98 case iana.ProtocolICMP: 99 return &PacketConn{c: c, p4: ipv4.NewPacketConn(c)}, nil 100 case iana.ProtocolIPv6ICMP: 101 return &PacketConn{c: c, p6: ipv6.NewPacketConn(c)}, nil 102 default: 103 return &PacketConn{c: c}, nil 104 } 105 }