1 // Copyright 2012 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.
5 // Package icmp provides basic functions for the manipulation of
6 // messages used in the Internet Control Message Protocols,
9 // ICMPv4 and ICMPv6 are defined in RFC 792 and RFC 4443.
10 // Multi-part message support for ICMP is defined in RFC 4884.
11 // ICMP extensions for MPLS are defined in RFC 4950.
12 // ICMP extensions for interface and next-hop identification are
13 // defined in RFC 5837.
14 package icmp // import "golang.org/x/net/icmp"
22 "golang.org/x/net/internal/iana"
23 "golang.org/x/net/ipv4"
24 "golang.org/x/net/ipv6"
27 // BUG(mikio): This package is not implemented on NaCl and Plan 9.
30 errMessageTooShort = errors.New("message too short")
31 errHeaderTooShort = errors.New("header too short")
32 errBufferTooShort = errors.New("buffer too short")
33 errOpNoSupport = errors.New("operation not supported")
34 errNoExtension = errors.New("no extension")
35 errInvalidExtension = errors.New("invalid extension")
38 func checksum(b []byte) uint16 {
39 csumcv := len(b) - 1 // checksum coverage
41 for i := 0; i < csumcv; i += 2 {
42 s += uint32(b[i+1])<<8 | uint32(b[i])
45 s += uint32(b[csumcv])
52 // A Type represents an ICMP message type.
57 // A Message represents an ICMP message.
59 Type Type // type, either ipv4.ICMPType or ipv6.ICMPType
61 Checksum int // checksum
62 Body MessageBody // body
65 // Marshal returns the binary encoding of the ICMP message m.
67 // For an ICMPv4 message, the returned message always contains the
68 // calculated checksum field.
70 // For an ICMPv6 message, the returned message contains the calculated
71 // checksum field when psh is not nil, otherwise the kernel will
72 // compute the checksum field during the message transmission.
73 // When psh is not nil, it must be the pseudo header for IPv6.
74 func (m *Message) Marshal(psh []byte) ([]byte, error) {
76 switch typ := m.Type.(type) {
82 return nil, syscall.EINVAL
84 b := []byte{byte(mtype), byte(m.Code), 0, 0}
85 if m.Type.Protocol() == iana.ProtocolIPv6ICMP && psh != nil {
88 if m.Body != nil && m.Body.Len(m.Type.Protocol()) != 0 {
89 mb, err := m.Body.Marshal(m.Type.Protocol())
95 if m.Type.Protocol() == iana.ProtocolIPv6ICMP {
96 if psh == nil { // cannot calculate checksum here
99 off, l := 2*net.IPv6len, len(b)-len(psh)
100 binary.BigEndian.PutUint32(b[off:off+4], uint32(l))
103 // Place checksum back in header; using ^= avoids the
104 // assumption the checksum bytes are zero.
105 b[len(psh)+2] ^= byte(s)
106 b[len(psh)+3] ^= byte(s >> 8)
107 return b[len(psh):], nil
110 var parseFns = map[Type]func(int, []byte) (MessageBody, error){
111 ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach,
112 ipv4.ICMPTypeTimeExceeded: parseTimeExceeded,
113 ipv4.ICMPTypeParameterProblem: parseParamProb,
115 ipv4.ICMPTypeEcho: parseEcho,
116 ipv4.ICMPTypeEchoReply: parseEcho,
118 ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach,
119 ipv6.ICMPTypePacketTooBig: parsePacketTooBig,
120 ipv6.ICMPTypeTimeExceeded: parseTimeExceeded,
121 ipv6.ICMPTypeParameterProblem: parseParamProb,
123 ipv6.ICMPTypeEchoRequest: parseEcho,
124 ipv6.ICMPTypeEchoReply: parseEcho,
127 // ParseMessage parses b as an ICMP message.
128 // Proto must be either the ICMPv4 or ICMPv6 protocol number.
129 func ParseMessage(proto int, b []byte) (*Message, error) {
131 return nil, errMessageTooShort
134 m := &Message{Code: int(b[1]), Checksum: int(binary.BigEndian.Uint16(b[2:4]))}
136 case iana.ProtocolICMP:
137 m.Type = ipv4.ICMPType(b[0])
138 case iana.ProtocolIPv6ICMP:
139 m.Type = ipv6.ICMPType(b[0])
141 return nil, syscall.EINVAL
143 if fn, ok := parseFns[m.Type]; !ok {
144 m.Body, err = parseDefaultMessageBody(proto, b[4:])
146 m.Body, err = fn(proto, b[4:])