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