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