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. 4 5// Package icmp provides basic functions for the manipulation of 6// messages used in the Internet Control Message Protocols, 7// ICMPv4 and ICMPv6. 8// 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// PROBE: A utility for probing interfaces is defined in RFC 8335. 15package icmp // import "golang.org/x/net/icmp" 16 17import ( 18 "encoding/binary" 19 "errors" 20 "net" 21 22 "golang.org/x/net/internal/iana" 23 "golang.org/x/net/ipv4" 24 "golang.org/x/net/ipv6" 25) 26 27// BUG(mikio): This package is not implemented on JS, NaCl and Plan 9. 28 29var ( 30 errInvalidConn = errors.New("invalid connection") 31 errInvalidProtocol = errors.New("invalid protocol") 32 errMessageTooShort = errors.New("message too short") 33 errHeaderTooShort = errors.New("header too short") 34 errBufferTooShort = errors.New("buffer too short") 35 errOpNoSupport = errors.New("operation not supported") 36 errNoExtension = errors.New("no extension") 37 errInvalidExtension = errors.New("invalid extension") 38) 39 40func checksum(b []byte) uint16 { 41 csumcv := len(b) - 1 // checksum coverage 42 s := uint32(0) 43 for i := 0; i < csumcv; i += 2 { 44 s += uint32(b[i+1])<<8 | uint32(b[i]) 45 } 46 if csumcv&1 == 0 { 47 s += uint32(b[csumcv]) 48 } 49 s = s>>16 + s&0xffff 50 s = s + s>>16 51 return ^uint16(s) 52} 53 54// A Type represents an ICMP message type. 55type Type interface { 56 Protocol() int 57} 58 59// A Message represents an ICMP message. 60type Message struct { 61 Type Type // type, either ipv4.ICMPType or ipv6.ICMPType 62 Code int // code 63 Checksum int // checksum 64 Body MessageBody // body 65} 66 67// Marshal returns the binary encoding of the ICMP message m. 68// 69// For an ICMPv4 message, the returned message always contains the 70// calculated checksum field. 71// 72// For an ICMPv6 message, the returned message contains the calculated 73// checksum field when psh is not nil, otherwise the kernel will 74// compute the checksum field during the message transmission. 75// When psh is not nil, it must be the pseudo header for IPv6. 76func (m *Message) Marshal(psh []byte) ([]byte, error) { 77 var mtype int 78 switch typ := m.Type.(type) { 79 case ipv4.ICMPType: 80 mtype = int(typ) 81 case ipv6.ICMPType: 82 mtype = int(typ) 83 default: 84 return nil, errInvalidProtocol 85 } 86 b := []byte{byte(mtype), byte(m.Code), 0, 0} 87 if m.Type.Protocol() == iana.ProtocolIPv6ICMP && psh != nil { 88 b = append(psh, b...) 89 } 90 if m.Body != nil && m.Body.Len(m.Type.Protocol()) != 0 { 91 mb, err := m.Body.Marshal(m.Type.Protocol()) 92 if err != nil { 93 return nil, err 94 } 95 b = append(b, mb...) 96 } 97 if m.Type.Protocol() == iana.ProtocolIPv6ICMP { 98 if psh == nil { // cannot calculate checksum here 99 return b, nil 100 } 101 off, l := 2*net.IPv6len, len(b)-len(psh) 102 binary.BigEndian.PutUint32(b[off:off+4], uint32(l)) 103 } 104 s := checksum(b) 105 // Place checksum back in header; using ^= avoids the 106 // assumption the checksum bytes are zero. 107 b[len(psh)+2] ^= byte(s) 108 b[len(psh)+3] ^= byte(s >> 8) 109 return b[len(psh):], nil 110} 111 112var parseFns = map[Type]func(int, Type, []byte) (MessageBody, error){ 113 ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach, 114 ipv4.ICMPTypeTimeExceeded: parseTimeExceeded, 115 ipv4.ICMPTypeParameterProblem: parseParamProb, 116 117 ipv4.ICMPTypeEcho: parseEcho, 118 ipv4.ICMPTypeEchoReply: parseEcho, 119 ipv4.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest, 120 ipv4.ICMPTypeExtendedEchoReply: parseExtendedEchoReply, 121 122 ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach, 123 ipv6.ICMPTypePacketTooBig: parsePacketTooBig, 124 ipv6.ICMPTypeTimeExceeded: parseTimeExceeded, 125 ipv6.ICMPTypeParameterProblem: parseParamProb, 126 127 ipv6.ICMPTypeEchoRequest: parseEcho, 128 ipv6.ICMPTypeEchoReply: parseEcho, 129 ipv6.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest, 130 ipv6.ICMPTypeExtendedEchoReply: parseExtendedEchoReply, 131} 132 133// ParseMessage parses b as an ICMP message. 134// The provided proto must be either the ICMPv4 or ICMPv6 protocol 135// number. 136func ParseMessage(proto int, b []byte) (*Message, error) { 137 if len(b) < 4 { 138 return nil, errMessageTooShort 139 } 140 var err error 141 m := &Message{Code: int(b[1]), Checksum: int(binary.BigEndian.Uint16(b[2:4]))} 142 switch proto { 143 case iana.ProtocolICMP: 144 m.Type = ipv4.ICMPType(b[0]) 145 case iana.ProtocolIPv6ICMP: 146 m.Type = ipv6.ICMPType(b[0]) 147 default: 148 return nil, errInvalidProtocol 149 } 150 if fn, ok := parseFns[m.Type]; !ok { 151 m.Body, err = parseDefaultMessageBody(proto, b[4:]) 152 } else { 153 m.Body, err = fn(proto, m.Type, b[4:]) 154 } 155 if err != nil { 156 return nil, err 157 } 158 return m, nil 159} 160