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