| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows |
| |
| package icmp |
| |
| import ( |
| "net" |
| "os" |
| "runtime" |
| "syscall" |
| |
| "golang.org/x/net/internal/iana" |
| "golang.org/x/net/ipv4" |
| "golang.org/x/net/ipv6" |
| ) |
| |
| const sysIP_STRIPHDR = 0x17 // for now only darwin supports this option |
| |
| // ListenPacket listens for incoming ICMP packets addressed to |
| // address. See net.Dial for the syntax of address. |
| // |
| // For non-privileged datagram-oriented ICMP endpoints, network must |
| // be "udp4" or "udp6". The endpoint allows to read, write a few |
| // limited ICMP messages such as echo request and echo reply. |
| // Currently only Darwin and Linux support this. |
| // |
| // Examples: |
| // |
| // ListenPacket("udp4", "192.168.0.1") |
| // ListenPacket("udp4", "0.0.0.0") |
| // ListenPacket("udp6", "fe80::1%en0") |
| // ListenPacket("udp6", "::") |
| // |
| // For privileged raw ICMP endpoints, network must be "ip4" or "ip6" |
| // followed by a colon and an ICMP protocol number or name. |
| // |
| // Examples: |
| // |
| // ListenPacket("ip4:icmp", "192.168.0.1") |
| // ListenPacket("ip4:1", "0.0.0.0") |
| // ListenPacket("ip6:ipv6-icmp", "fe80::1%en0") |
| // ListenPacket("ip6:58", "::") |
| func ListenPacket(network, address string) (*PacketConn, error) { |
| var family, proto int |
| switch network { |
| case "udp4": |
| family, proto = syscall.AF_INET, iana.ProtocolICMP |
| case "udp6": |
| family, proto = syscall.AF_INET6, iana.ProtocolIPv6ICMP |
| default: |
| i := last(network, ':') |
| if i < 0 { |
| i = len(network) |
| } |
| switch network[:i] { |
| case "ip4": |
| proto = iana.ProtocolICMP |
| case "ip6": |
| proto = iana.ProtocolIPv6ICMP |
| } |
| } |
| var cerr error |
| var c net.PacketConn |
| switch family { |
| case syscall.AF_INET, syscall.AF_INET6: |
| s, err := syscall.Socket(family, syscall.SOCK_DGRAM, proto) |
| if err != nil { |
| return nil, os.NewSyscallError("socket", err) |
| } |
| if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && family == syscall.AF_INET { |
| if err := syscall.SetsockoptInt(s, iana.ProtocolIP, sysIP_STRIPHDR, 1); err != nil { |
| syscall.Close(s) |
| return nil, os.NewSyscallError("setsockopt", err) |
| } |
| } |
| sa, err := sockaddr(family, address) |
| if err != nil { |
| syscall.Close(s) |
| return nil, err |
| } |
| if err := syscall.Bind(s, sa); err != nil { |
| syscall.Close(s) |
| return nil, os.NewSyscallError("bind", err) |
| } |
| f := os.NewFile(uintptr(s), "datagram-oriented icmp") |
| c, cerr = net.FilePacketConn(f) |
| f.Close() |
| default: |
| c, cerr = net.ListenPacket(network, address) |
| } |
| if cerr != nil { |
| return nil, cerr |
| } |
| switch proto { |
| case iana.ProtocolICMP: |
| return &PacketConn{c: c, p4: ipv4.NewPacketConn(c)}, nil |
| case iana.ProtocolIPv6ICMP: |
| return &PacketConn{c: c, p6: ipv6.NewPacketConn(c)}, nil |
| default: |
| return &PacketConn{c: c}, nil |
| } |
| } |