blob: 7fac4f9652415b705203cc78fda82bda01e7b472 [file] [log] [blame]
David K. Bainbridge215e0242017-09-05 23:18:24 -07001// 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// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
6
7package icmp
8
9import (
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
20const 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// ListenPacket("udp4", "192.168.0.1")
32// ListenPacket("udp4", "0.0.0.0")
33// ListenPacket("udp6", "fe80::1%en0")
34// ListenPacket("udp6", "::")
35//
36// For privileged raw ICMP endpoints, network must be "ip4" or "ip6"
37// followed by a colon and an ICMP protocol number or name.
38//
39// Examples:
40// ListenPacket("ip4:icmp", "192.168.0.1")
41// ListenPacket("ip4:1", "0.0.0.0")
42// ListenPacket("ip6:ipv6-icmp", "fe80::1%en0")
43// ListenPacket("ip6:58", "::")
44func ListenPacket(network, address string) (*PacketConn, error) {
45 var family, proto int
46 switch network {
47 case "udp4":
48 family, proto = syscall.AF_INET, iana.ProtocolICMP
49 case "udp6":
50 family, proto = syscall.AF_INET6, iana.ProtocolIPv6ICMP
51 default:
52 i := last(network, ':')
53 switch network[:i] {
54 case "ip4":
55 proto = iana.ProtocolICMP
56 case "ip6":
57 proto = iana.ProtocolIPv6ICMP
58 }
59 }
60 var cerr error
61 var c net.PacketConn
62 switch family {
63 case syscall.AF_INET, syscall.AF_INET6:
64 s, err := syscall.Socket(family, syscall.SOCK_DGRAM, proto)
65 if err != nil {
66 return nil, os.NewSyscallError("socket", err)
67 }
68 if runtime.GOOS == "darwin" && family == syscall.AF_INET {
69 if err := syscall.SetsockoptInt(s, iana.ProtocolIP, sysIP_STRIPHDR, 1); err != nil {
70 syscall.Close(s)
71 return nil, os.NewSyscallError("setsockopt", err)
72 }
73 }
74 sa, err := sockaddr(family, address)
75 if err != nil {
76 syscall.Close(s)
77 return nil, err
78 }
79 if err := syscall.Bind(s, sa); err != nil {
80 syscall.Close(s)
81 return nil, os.NewSyscallError("bind", err)
82 }
83 f := os.NewFile(uintptr(s), "datagram-oriented icmp")
84 c, cerr = net.FilePacketConn(f)
85 f.Close()
86 default:
87 c, cerr = net.ListenPacket(network, address)
88 }
89 if cerr != nil {
90 return nil, cerr
91 }
92 switch proto {
93 case iana.ProtocolICMP:
94 return &PacketConn{c: c, p4: ipv4.NewPacketConn(c)}, nil
95 case iana.ProtocolIPv6ICMP:
96 return &PacketConn{c: c, p6: ipv6.NewPacketConn(c)}, nil
97 default:
98 return &PacketConn{c: c}, nil
99 }
100}